Skip to navigation

Accessing Logs

View as Markdown

Use the CLI or TUI for recent supervisor logs, and compute-driver tooling for supervisor files, sandbox-runtime events, and main process output. The supervisor runs separately from the sandbox workload, so these logs have different locations and collection paths.

CLI

Use openshell logs to stream logs from a running sandbox:

openshell logs smoke-l4 --source sandbox

The CLI receives logs from the gateway over gRPC. Each line includes a timestamp, source, level, and message:

[1775014132.118] [sandbox] [OCSF ] [ocsf] NET:OPEN [INFO] ALLOWED /usr/bin/curl(58) -> api.github.com:443 [policy:github_api engine:opa]
[1775014132.190] [sandbox] [OCSF ] [ocsf] HTTP:GET [INFO] ALLOWED GET http://api.github.com/zen [policy:github_api]
[1775014132.690] [sandbox] [OCSF ] [ocsf] NET:OPEN [MED] DENIED /usr/bin/curl(64) -> httpbin.org:443 [policy:- engine:opa]
[1775014113.058] [sandbox] [INFO ] [openshell_sandbox] Starting sandbox

OCSF structured events show OCSF as the level. Standard tracing events show INFO, WARN, or ERROR.

Gateway-originated policy mutations also appear in this stream. When the gateway merges openshell policy update operations or approves or removes draft policy chunks, it emits gateway OCSF CONFIG:* lines for the affected sandbox so you can see the exact logical change that produced a new policy revision.

TUI

The TUI dashboard displays sandbox logs in real time. Logs appear in the log panel with the same format as the CLI.

Main Process Output

The sandbox’s main process writes its stdout and stderr to the sandbox container’s stdout and stderr, alongside sandbox-runtime diagnostics. Read this output with the container tooling for the compute driver:

kubectl -n <namespace> logs <sandbox-pod> -c agent
docker logs <sandbox-container>
podman logs <sandbox-container>

The sandbox runtime also emits OCSF shorthand events, including process-launch and Landlock filesystem events, into these container logs when its diagnostic log filter permits them. These runtime-originated events are not pushed to the gateway log stream or included in the supervisor’s JSONL file. Collect workload container logs separately from supervisor logs to retain both sources.

A main process started with a TTY writes only to its attachment; use openshell sandbox connect to view that output. Output from sandbox exec and SSH sessions does not reach the container log, and openshell logs does not include main process output.

When the container runtime falls behind reading the log, the main process blocks on writes, as with any container.

Gateway Log Storage

The supervisor pushes logs to the gateway over gRPC in real time. The gateway stores up to 2,000 recent log lines per sandbox in memory. This buffer is not persisted to disk and is lost when the gateway restarts or the sandbox’s buffers are removed. The push channel is bounded and can drop events under load or during disconnection.

For durable retention, collect supervisor output or files and workload container logs into an external log store. The local files rotate and use temporary storage on container drivers; they are not a durable archive. See Direct Filesystem Access for locations and collection limits, and OCSF JSON export for structured file output.

The gateway’s separate ocsf_log configuration writes gateway-produced OCSF events to JSONL. It does not persist supervisor log lines received over gRPC or main process output.

Loss Awareness and Resume

The watch stream behind openshell logs is loss-aware. Each resumable event (log line or platform event) carries an opaque cursor token. Status snapshots and warnings carry an empty cursor.

Do not parse a cursor. The only supported operation is comparing two cursors observed on the same stream and keeping the greater one as the resume point. That comparison is a plain string comparison in any language.

The gateway distinguishes recoverable from unrecoverable loss:

  • Recoverable lag. When a consumer falls behind and the gateway skips ahead in its buffer, the stream emits a warning event and keeps running. The warning is the signal that events were skipped.
  • Unrecoverable gap. When a client reconnects and asks to resume after a cursor the gateway has already trimmed from its buffer, the stream ends with an OUT_OF_RANGE status. The client should restart observation and, if it needs the missing lines, check collected logs or any remaining supervisor files. Those sources may also have gaps.

A cursor is bound to the cursor space that issued it. A gateway restart, teardown of the sandbox’s buffers, or a reconnect that lands on a different gateway replica starts a new space, and cursors from the previous one no longer refer to anything. The gateway rejects them with OUT_OF_RANGE instead of treating them as caught up — which would silently suppress the new space’s events. A malformed cursor is rejected with INVALID_ARGUMENT.

OUT_OF_RANGE is terminal for that cursor. Restart the watch with an empty resume cursor; retrying the same one fails identically.

On reconnect, a client passes the highest cursor it processed as the resume point. The gateway replays only events after that cursor — logs and platform events merged in order — then resumes live delivery. The handoff from replay to live delivery is exact: an event buffered while the stream was reopening is delivered once, never twice. It is not a guarantee that nothing was lost — a warning event or an OUT_OF_RANGE status still reports loss, both before and after a reconnect.

The gateway merges the log and platform event sources before emitting, so events normally arrive in ascending cursor order. That applies to the buffered tail you receive when the stream opens, to a replay after a resume, and to live delivery. The gateway does not delay an event to wait for a lower cursor that has not been published yet, so a cursor can still arrive late under concurrent publication. Track the highest cursor seen as the resume point rather than the last one received.

Direct Filesystem Access

The supervisor writes shorthand logs to /var/log/openshell.YYYY-MM-DD.log and, when enabled, OCSF JSONL to /var/log/openshell-ocsf.YYYY-MM-DD.log in its own filesystem. openshell sandbox exec runs in the workload and does not give access to the supervisor’s files.

Compute driverSupervisor log locationLocal storage lifetime
Kubernetes/var/log in the separate supervisor pod’s supervisor containerAn emptyDir volume, removed with the pod.
Docker / Podman/var/log in the separate supervisor containerA tmpfs mount, lost when the container stops.
VM/var/log on the host running the supervisor, if writableHost-managed storage. The driver also captures stderr in supervisor.err.log in the sandbox’s driver state directory.
Windows MXCGateway-local JSONL outputSee Windows MXC gateways.

Use platform tooling with access to the supervisor resources. For Podman, copy the current supervisor files to a local directory while the container is running:

mkdir -p ./supervisor-logs
podman cp <supervisor-container>:/var/log/. ./supervisor-logs/

Docker’s docker cp cannot copy the live contents of the supervisor’s tmpfs log mount. Retrieving those files requires operator-managed access to the live mount on the Docker daemon host. The default supervisor image has no shell or tar, so shell-based docker exec collection is not an alternative.

For live console output, read the supervisor’s stderr through the platform log interface:

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

Shorthand and diagnostics follow the supervisor’s diagnostic log filter. With ocsf_json_enabled=true, stderr also includes compact OCSF-JSON records independently of that filter and file storage. See OCSF JSON collection for the collector format and delivery limits. A cluster or container log collector can ship this output for retention. Collect workload container logs separately for main process output and sandbox-runtime events.

The default supervisor image has no shell or tar, so shell-based kubectl exec and kubectl cp examples do not work with that image. Reading Kubernetes supervisor files requires operator-managed access to its log volume, such as cluster debugging tooling. OpenShell does not configure a file collector for you.

Ship files before rotation or resource teardown and configure your collector to handle daily rotation. Local file writers are non-blocking and can also drop events under pressure. If the supervisor cannot open /var/log, it falls back to stderr-only logging and does not produce these files, even when ocsf_json_enabled is true. Neither the local files nor the gateway stream guarantees complete audit history.

Filtering by Event Type

The shorthand format is designed for grep. After copying supervisor files into ./supervisor-logs, use these patterns:

# All denied connections
grep "DENIED\|BLOCKED" ./supervisor-logs/openshell.*.log
# All network events
grep "OCSF NET:" ./supervisor-logs/openshell.*.log
# All L7 enforcement decisions
grep "OCSF HTTP:" ./supervisor-logs/openshell.*.log
# Security findings only
grep "OCSF FINDING:" ./supervisor-logs/openshell.*.log
# Policy changes
grep "OCSF CONFIG:" ./supervisor-logs/openshell.*.log
# All OCSF events, excluding standard tracing
grep "^.* OCSF " ./supervisor-logs/openshell.*.log
# Events at medium severity or above
grep "\[MED\]\|\[HIGH\]\|\[CRIT\]\|\[FATAL\]" ./supervisor-logs/openshell.*.log

Next Steps