Skip to content

OCSF JSONL enabled and OCSF events emitted, but /var/log/openshell-ocsf.* is not created on Docker Desktop / WSL2 #3895

Description

@BondarenkoIV

User Story

On Docker Desktop / WSL2 with the Docker compute driver, I need the documented OCSF JSONL file to be available in the workload sandbox after enabling ocsf_json_enabled. OCSF events are emitted, but the file does not appear.

Problem Statement

Expected

With:

openshell settings set <sandbox> --key ocsf_json_enabled --value true

OpenShell documentation says OCSF JSON records should be written to:

/var/log/openshell-ocsf.YYYY-MM-DD.log

Observed

The setting is applied successfully. Real OCSF events are emitted, including CONFIG:*, NET:*, policy revision changes and SSH relay events. However, /var/log/openshell-ocsf.YYYY-MM-DD.log is never created inside the workload sandbox. Reproduced after pinning both gateway and supervisor to 0.1.1.

Impact / Why This Matters

The documented JSONL file is unavailable inside the workload sandbox, despite effective configuration and live OCSF events. The gateway stream contains real events. Is JSONL export expected to work with the Docker Desktop / WSL2 Docker driver path in 0.1.1? If yes, which filesystem/container should own /var/log/openshell-ocsf.* in this topology?

Acceptance Criteria

With ocsf_json_enabled=true, the documented /var/log/openshell-ocsf.YYYY-MM-DD.log appears in the expected filesystem for this topology and contains the emitted OCSF JSON records, or the supported behavior and file location are clarified.

Reproduction Steps

  1. Start a real sandbox using the Docker compute driver on Windows 11 / WSL2 Ubuntu / Docker Desktop, with OpenShell CLI 0.1.1 and gateway/supervisor pinned to 0.1.1.
  2. Run openshell settings set <sandbox> --key ocsf_json_enabled --value true.
  3. Confirm policy updates and OCSF activity (CONFIG:*, NET:*, policy revision changes, SSH relay events); the gateway stream contains real events.
  4. Check for /var/log/openshell-ocsf.YYYY-MM-DD.log inside the workload sandbox. It does not appear.

Additional Docker Desktop / WSL2 observation: host.openshell.internal did not resolve from the supervisor path. The sandbox became operational only after changing the Docker driver callback endpoint to http://127.0.0.1:8080 with host networking. This may or may not be related to the JSONL issue.

Environment

  • Windows 11
  • WSL2 Ubuntu
  • Docker Desktop
  • OpenShell CLI 0.1.1
  • gateway/supervisor pinned to 0.1.1
  • Docker compute driver

Logs

Supervisor logs show:


CONFIG:UPDATED ... key:ocsf_json_enabled old:<unset> new:true
OCSF JSONL logging toggled

Activity

  1. added theissue type on Sep 29, 2026
  2. BondarenkoIV commented on Sep 29, 2026

    @BondarenkoIV
    Author

    Thanks — I reran this against a fresh 0.1.1 Docker Desktop / WSL2 sandbox and narrowed it down further.

    The supervisor container is:

    User=65534:65534
    ReadonlyRootfs=true
    

    and Docker configures:

    /var/log = rw,noexec,nosuid,size=64m,uid=65534,gid=65534,mode=0700
    

    so the configured tmpfs is intended to be writable by the supervisor UID.

    I then enabled JSONL:

    CONFIG:UPDATED ... key:ocsf_json_enabled old:<unset> new:true
    CONFIG:DETECTED ...
    OCSF JSONL logging toggled
    

    No openshell-ocsf.*.log appeared.

    To make sure this was not just a lack of OCSF events after enabling it, I then changed the setting from true to false.

    The supervisor emitted:

    CONFIG:UPDATED ... key:ocsf_json_enabled old:true new:false
    CONFIG:DETECTED ...
    OCSF JSONL logging toggled
    

    and there was still no JSONL file.

    In v0.1.1, log_setting_changes(...) and the CONFIG:DETECTED OCSF emission happen before apply_ocsf_json_setting(...), so those events should hit OcsfJsonlLayer while the shared enabled flag is still true.

    One additional observation: copying /var/log out of the supervisor showed no openshell* files at all.

    The failure may therefore be in file/appender creation or the actual write path rather than the toggle itself.

    I also noticed that the relevant failures are currently hard to observe:

    .build("/var/log").ok()

    discards the JSONL appender construction error, and OcsfJsonlLayer does:

    let _ = w.write_all(line.as_bytes());

    which discards write errors.

    Instrumenting those two paths may expose the exact Docker Desktop/tmpfs failure.

  3. shalevyoni-ops commented on Oct 4, 2026

    @shalevyoni-ops

    Still reproduces on v0.1.2 (you tested 0.1.1), and while confirming it I found that the other documented OCSF route is not available on that release either. Posting both in case the pair is useful.

    1. Sandbox JSONL — reproduces on v0.1.2

    Same topology as yours (Windows 11 / WSL2 / Docker Desktop, compute_driver = "docker"), CLI and gateway both 0.1.2, kernel 6.6.114.1-microsoft-standard-WSL2.

    Enabled globally before the sandbox existed, then recreated the sandbox so the setting was present from birth — in case the toggle only takes effect at supervisor startup:

    $ openshell settings set --global --key ocsf_json_enabled --value true --yes
    ✓ Set global setting ocsf_json_enabled=true (revision 1)
    
    $ openshell settings get --global | grep ocsf
      ocsf_json_enabled = true
      ocsf_schema_version = <unset>
    
    $ openshell sandbox delete demo
    ✓ Sandbox demo deletion accepted; cleanup is pending
    $ openshell sandbox create --name demo --policy policy.yaml
    $ openshell sandbox list
    NAME  CREATED              PHASE
    demo  2026-10-04 20:03:12  Ready

    Then drove real L7 traffic out of the sandbox (one request allowed, one denied by a supervisor middleware registered on the gateway), and OCSF events are definitely being produced:

    $ openshell logs demo | grep -oE '[A-Z][A-Z_]+:[A-Z_]+' | sort | uniq -c | sort -rn
          4 HTTP:POST
          3 NET:OPEN
          2 SSH:OPEN
          2 CONFIG:PUBLISHED
          2 CONFIG:CONFIGURED
          1 SSH:LISTEN
          1 NET:LISTEN
          1 FINDING:CREATE

    The documented file is still absent inside the workload sandbox:

    $ openshell sandbox exec -n demo -- bash -c 'ls /var/log/openshell-ocsf*'
    ls: cannot access '/var/log/openshell-ocsf*': No such file or directory
    
    $ openshell sandbox exec -n demo -- bash -c 'ls -la /var/log/'
    total 264
    -rw-r--r-- 1 root root   4860 Sep  5 13:01 alternatives.log
    drwxr-xr-x 1 root root   4096 Sep 14 04:54 apt
    -rw-r--r-- 1 root root  61229 Sep  5 12:55 bootstrap.log
    -rw-rw---- 1 root utmp      0 Sep  5 12:55 btmp
    -rw-r--r-- 1 root root 187630 Sep 14 04:54 dpkg.log
    ...

    No openshell-* file of any name, not just a missing dated segment. So on this topology the behaviour is unchanged from 0.1.1 to 0.1.2.

    2. The gateway-side route does not exist on v0.1.2

    docs/observability/ocsf-json-export.mdx on main documents a second, independent sink:

    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.

    That is the natural workaround for issue 1 — and the v0.1.2 gateway rejects the config outright:

    Error:   × failed to parse gateway config file '/root/.config/openshell/gateway.toml':
      TOML parse error at line 18, column 20
      18 | [openshell.gateway.ocsf_log]
         |                    ^^^^^^^^
      unknown field `ocsf_log`, expected one of `name`, `bind_address`,
      `health_bind_address`, `metrics_bind_address`, `log_level`, `compute_driver`,
      `credential_drivers`, `default_credential_driver`, `credential_storage`,
      `ssh_session_ttl_secs`, `grpc_rate_limit_requests`,
      `grpc_rate_limit_window_seconds`, `policy_validation_failure_mode`,
      `server_sans`, `enable_loopback_service_http`, `enable_websocket_tunnel`,
      `guest_tls_ca`, `guest_tls_cert`, `guest_tls_key`, `disable_tls`, `tls`,
      `oidc`, `auth`, `interceptors`, `provider_profile_sources`, `mtls_auth`,
      `gateway_jwt`, `otlp`, `database_url`

    It is present on the 0.1.3 line. Using the dev release (0.1.3-dev.82+g8983642e2), the same config parses, and the accepted-field list now ends:

    ... `mtls_auth`, `gateway_jwt`, `otlp`, `ocsf_log`, `database_url`
    

    Its sub-fields, read the same way:

    expected one of `path`, `schema_version`, `rotation`, `max_files`,
    `queue_capacity`, `queue_max_bytes`
    

    (Checked with a deliberately bogus key in each position; 0.1.3 still rejects unknown fields strictly, so the acceptance is a real schema change rather than relaxed parsing.)

    So, concretely: on the latest stable release both documented OCSF JSONL routes are unavailable on this topology — the sandbox file is never created, and the gateway sink is not in the config schema. That may be worth a line in the docs, since the page on main does not say the gateway sink requires >= 0.1.3.

    Questions

    1. Is sandbox JSONL export expected to work on the Docker driver under Docker Desktop / WSL2 in 0.1.2, or is that topology known-unsupported? Your UID/tmpfs finding on 0.1.1 suggests the supervisor should be able to write there.
    2. Is there a supported way to get gateway-origin OCSF JSONL on 0.1.2, or is 0.1.3 the floor for that?

    Happy to run further diagnostics on this setup — it reproduces consistently.

  4. em198 commented on Oct 6, 2026

    @em198

    On macOS (Docker Desktop, arm64), 0.1.2, Docker driver, the file is written — and both checks above would miss it, so it may be worth ruling out on your WSL2 setups before digging further.

    • The supervisor's /var/log is the tmpfs @BondarenkoIV found (rw,noexec,nosuid,size=64m,uid=65534,gid=65534,mode=0700) over a read-only rootfs. HostConfig.Tmpfs shows it; .Mounts doesn't.
    • docker cp reads the container's filesystem layers, not a tmpfs mounted over them, so copying /var/log out of the supervisor shows no openshell* files even when they're there.
    • openshell sandbox exec lands in the workload container, not the supervisor, so ls /var/log there won't show it either.

    Reading through the supervisor's own mount namespace from a privileged helper does:

    PID=$(docker inspect -f '{{.State.Pid}}' <sandbox>-supervisor)
    docker run --rm --privileged --pid=host ubuntu:24.04 \
      ls -la /proc/$PID/root/var/log/
    

    Here that listed openshell-ocsf.<date>.log — 49 JSONL records, 42 KB, covering network (4001/4002/4007), config state (5019) and relay open/close — next to openshell.<date>.log.

    I haven't tried WSL2, so I can't say it's there for you, but that one command would tell you. If it is, the gap on this driver is collection rather than emission:

    • the docs say the JSONL lands "inside the sandbox", but it's in the supervisor container, on a 64 MB tmpfs nobody can exec into;
    • it's lost when that container restarts;
    • there's no supported way to ship it from the host — and, as @shalevyoni-ops found, the gateway ocsf_log sink needs 0.1.3.

    If it's genuinely absent, +1 to instrumenting the two silent paths @BondarenkoIV pointed out (.build("/var/log").ok() and let _ = w.write_all).

  5. johntmyers commented on Oct 8, 2026

    @johntmyers
    Collaborator

    @BondarenkoIV 0.1.3 will now have OCSF-JSON logs available via stderr similarly to the shorthand OCSF logs. We have some future work coming that will optionally allow all OCSF logs to be accessible via the Gateway. We're also looking at how to incorporate something like https://github.com/NVIDIA-dev/OpenShell-exporter into the architecture.

    For now I would plan to tail the container logs to parse out the JSON records.

  6. BondarenkoIV commented on Oct 9, 2026

    @BondarenkoIV
    Author

    John, thank you — this is exactly the minimal interface we needed for the next validation step.
    We’ll target 0.1.3 and start with the approach you suggested: tail the container stderr, preserve the raw OCSF JSON records, and map them into our execution-evidence model without treating the log itself as stronger proof than it is.
    The main thing we want to test is whether we can reliably correlate a requested agent action with the observed runtime events/process/resource/result across a short multi-step execution.
    We’ll avoid building around the transport, so if OCSF later moves behind the Gateway/exporter, that should just replace the collection adapter rather than change the model.
    Once we’ve run that small validation, I’ll share what fields were sufficient, what correlation gaps remained, and where we still needed another evidence source.
    Thanks again for closing this loop.

  7. shalevyoni-ops commented on Oct 9, 2026

    @shalevyoni-ops

    Follow-up to my comment above (0.1.2, WSL2). Same test on a native Linux kernel with the 0.1.3 dev build: the file is still not created, by either mechanism.

    Environment

    • Kernel: 6.8.0-142-generic, 4 vCPU (cloud VM, OpenShell installed directly on the host; no WSL, no nested container)
    • CLI: openshell 0.1.3-dev.136+ge1f3c82ca
    • Gateway: openshell-gateway 0.1.3-dev.136+ge1f3c82ca (from the dev release .deb)
    • Sandbox: created fresh by this gateway, 2026-10-09 10:26:30 UTC, phase Ready

    Both documented mechanisms were enabled before the sandbox existed:

    $ openshell settings get --global | grep ocsf
      ocsf_json_enabled = true
      ocsf_schema_version = <unset>
    # gateway.toml
    [openshell.gateway.ocsf_log]
    path = "/var/log/openshell/gateway-ocsf.jsonl"

    The gateway accepts this config on 0.1.3 (on 0.1.2 it is rejected as an unknown field — see my earlier comment). /var/log/openshell existed before the gateway started and was mode 0777.

    Events are being emitted. After driving HTTP requests from inside the sandbox to a host endpoint:

    $ openshell logs demo | grep -oE '[A-Z][A-Z_]+:[A-Z_]+' | sort | uniq -c | sort -rn
          4 HTTP:POST
          3 SSH:OPEN
          3 NET:OPEN
          2 CONFIG:PUBLISHED
          1 SSH:LISTEN
          1 NET:LISTEN
          1 FINDING:CREATE
          1 CONFIG:READY
          1 CONFIG:LOADED
          1 CONFIG:ENRICHED
          1 CONFIG:ENABLED
          1 CONFIG:CONFIGURED

    Neither file exists:

    $ openshell sandbox exec -n demo -- bash -c 'ls -la /var/log/openshell-ocsf*'
    ls: cannot access '/var/log/openshell-ocsf*': No such file or directory
    
    $ openshell sandbox exec -n demo -- bash -c 'ls /var/log/'
    alternatives.log  apt  bootstrap.log  btmp  dpkg.log  faillog  lastlog  wtmp
    
    $ ls -la /var/log/openshell/
    total 8
    drwxrwxrwx  2 root root   4096 Oct  9 10:25 .
    drwxrwxr-x 10 root syslog 4096 Oct  9 10:25 ..

    What this run establishes

    • The sandbox-side file is not created on a native kernel with the 0.1.3 dev gateway and supervisor, with ocsf_json_enabled=true set before sandbox creation and OCSF events demonstrably emitted. So the behaviour reported in this issue is not specific to WSL2 or Docker Desktop.
    • The gateway-side [openshell.gateway.ocsf_log] sink, which the docs describe as collecting gateway-origin events even when no sandbox log stream applies, also produced no file in the same run.

    What it does not establish

    • Cause. We have not read the OpenShell source and are not guessing at one.
    • Anything about the MicroVM driver (ocsf_json_enabled silently writes nothing with the MicroVM driver (supervisor cannot open /var/log) #3855) or Windows MXC gateways; not tested.
    • Whether the gateway-side sink would have written anything given a different event mix. Only the events listed above occurred in this run.
    • Our sandbox policy also registered a gRPC middleware for the HTTP endpoint. We did not test without it, so we cannot say whether its presence matters.

    What I ran

    Reconstructed from the run above, not re-executed from a clean host — the VM has been destroyed.

    1. On a native Linux host with Docker: install the dev release (openshell-dev-amd64.deb from the releases page), confirm openshell --version and openshell-gateway --version both report 0.1.3-dev.
    2. Add to gateway.toml:
      [openshell.gateway.ocsf_log]
      path = "/var/log/openshell/gateway-ocsf.jsonl"
      mkdir -p /var/log/openshell; restart the gateway.
    3. openshell settings set --global --key ocsf_json_enabled --value true --yes
    4. Create a sandbox with a network policy that allows one HTTP endpoint on the host.
    5. From inside the sandbox, make a few HTTP requests to that endpoint; wait ~15 s.
    6. Confirm events: openshell logs <sandbox> | grep -oE '[A-Z][A-Z_]+:[A-Z_]+' | sort | uniq -c
    7. Check both paths:
      openshell sandbox exec -n <sandbox> -- ls -la /var/log/openshell-ocsf*
      ls -la /var/log/openshell/
      

    Happy to re-run with a specific build or extra logging if that helps.

  8. shalevyoni-ops commented on Oct 10, 2026

    @shalevyoni-ops

    Correction to my comment above. The sandbox-side check I used — openshell sandbox exec … ls /var/log/ — runs in the workload container. As @em198 showed on Oct 6, the supervisor writes the OCSF file into its own container's /var/log tmpfs, which that command cannot see. My conclusion that no sandbox-side file was created on the native kernel is withdrawn. The same applies to my Oct 4 comment on 0.1.2, which used the same check.

    What stands is the host-side observation: /var/log/openshell/ on the gateway host was empty after the run, with [openshell.gateway.ocsf_log] configured and accepted by the 0.1.3-dev.136 gateway. That path is on the host and is unaffected by the container question. I cannot re-run @em198's /proc/<pid>/root/var/log/ check — the VM has been destroyed.

    I also posted onto a thread that had already been closed the day before. Apologies for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions