CubeEgress JSONL Audit Logs: Complete Field Reference and Schema

CubeEgress JSONL audit logs capture every HTTP/HTTPS request traversing the transparent proxy as line-delimited JSON records containing timestamps, request IDs, sandbox identity, destination connection details, TLS metadata, HTTP metrics, policy decisions, and security events.

CubeEgress is the transparent proxy component of the TencentCloud CubeSandbox repository that intercepts outbound traffic from sandboxed workloads. Each request is recorded in /data/log/cube-egress/access.jsonl by the Lua module CubeEgress/lua/audit.lua, producing a comprehensive audit trail that includes the policy decision, credential injection actions, and any security-related denials.

Core Log Fields

Temporal and Identity Fields

Every log entry begins with precise timing and unique identification. The ts field contains an ISO-8601 timestamp in UTC, generated at lines 57-60 of CubeEgress/lua/audit.lua. The request_id field provides a unique identifier for the request within the worker, built from ngx.var.request_id when available, otherwise constructed from the connection ID and request counter (lines 77-88).

Network and Connection Metadata

The sandbox object identifies the source workload, containing src_ip (the sandbox's IP address) and policy_id (the UUID of the attached policy). The conn field captures the original destination before any routing, exposing original_dst_ip and original_dst_port (lines 65-68).

TLS Session Details

For encrypted connections, the tls object records the Server Name Indication (sni), negotiated cipher suite (cipher), TLS protocol version (version), and optional client ALPN preferences (lines 69-78). These fields enable compliance auditing and security forensics on HTTPS traffic.

HTTP Transaction Metrics

The http field contains request and response metadata: the HTTP method, target host (cube_audit_host), request path (request_uri), response status code, byte counts (req_bytes, resp_bytes), and the User-Agent header (lines 79-87). This data is assembled in _M.write_one() to provide complete HTTP visibility.

Policy Decision and Latency

The policy object records which rule matched (matched_rule), the final decision (allow, deny, or unknown), and the processing latency in microseconds (duration_us). This propagation is defined in network-agent/internal/cubeegress/wire.go (lines 88-90), allowing operators to audit enforcement actions and performance characteristics.

Redacted and Reserved Fields

The audit schema includes redacted_request_headers, redacted_request_body, and redacted_response_body fields. These are processed by the helper redactor module to ensure sensitive data is stripped. Currently, body fields are always set to null and reserved for future "full-audit" modes, while headers are redacted according to configured policies (lines 98-102).

Security-Specific Records

Credential Injection Metadata

When a request is allowed and performs secret injection, the credentials field lists the header names that were injected and their corresponding secret IDs. Importantly, the actual secret values are never logged, only the metadata required for audit compliance (lines 119-131).

Security Events and Denials

The security field appears only when noteworthy events occur, such as upstream verification failures or injection errors. It contains the reason (e.g., "upstream_unreachable_or_unverified", "secret_not_found"), along with inject_dropped and inject_skipped arrays detailing specific header injection failures (lines 45-55).

Record Types and Event Categories

Standard HTTP Requests

The default http_request record type is emitted by _M.write_one() for every proxied request. This comprehensive record merges all core fields into a single JSON line. If a request results in a 502/504 error with zero upstream bytes, the system automatically injects a security reason and emits a separate security_event line to ensure the failure is captured (lines 22-55).

Security Events

Explicit security_event records are generated via _M.write_security_event() for events that may not survive the normal log phase, such as early connection failures or injection errors. These records repeat critical fields (sandbox, conn, tls, http, credentials, policy, security) to guarantee event capture even when the standard request logging path is interrupted (lines 73-84).

TLS Handshake Failures

When TLS handshakes fail (e.g., certificate validation errors), the system generates tls_handshake records containing minimal fields: ts, request_id, event, sandbox, conn, tls, and security. This lightweight format ensures compliance logging without exposing incomplete HTTP data (lines 35-45).

Log Writing Mechanism

Initialization and File Handling

The audit module initializes with a configurable FILE_PATH defaulting to /data/log/cube-egress/access.jsonl, configured in CubeEgress/nginx.conf. During the init_worker phase, the module performs a test-open to verify writeability; failure aborts the worker to prevent silent audit loss (lines 34-45). The CubeEgress/start.sh script ensures the host directory exists with proper ownership before container startup.

Atomic Appends and Concurrency

Per-request logging occurs in the log_by_lua phase, which calls _M.write_one(). The function constructs the record using build_record, encodes it with cjson.safe, and atomically appends the line via io.open(..., "a"). Writes are atomic because the encoded JSON never exceeds the PIPE_BUF limit (approximately 4 KB), ensuring line integrity under concurrent load (lines 9-21).

Parsing and Querying Examples

To analyze these logs, you can parse the JSONL format using standard tools. Here is a Go struct matching the audit schema:

type AuditRecord struct {
    Ts        string `json:"ts"`
    RequestID string `json:"request_id"`
    Event     string `json:"event"`
    Sandbox   struct {
        SrcIP    string `json:"src_ip"`
        PolicyID string `json:"policy_id"`
    } `json:"sandbox"`
    Conn struct {
        OriginalDstIP   string `json:"original_dst_ip"`
        OriginalDstPort int    `json:"original_dst_port"`
    } `json:"conn"`
    TLS struct {
        SNI     string `json:"sni"`
        Cipher  string `json:"cipher"`
        Version string `json:"version"`
    } `json:"tls"`
    HTTP struct {
        Method    string `json:"method"`
        Host      string `json:"host"`
        Path      string `json:"path"`
        Status    int    `json:"status"`
        ReqBytes  int    `json:"req_bytes"`
        RespBytes int    `json:"resp_bytes"`
        UserAgent string `json:"user_agent"`
    } `json:"http"`
    Policy struct {
        MatchedRule string `json:"matched_rule"`
        Decision    string `json:"decision"`
        DurationUs  int    `json:"duration_us"`
    } `json:"policy"`
    Credentials *struct {
        Injected  []string `json:"injected"`
        SecretIDs []string `json:"secret_ids"`
    } `json:"credentials,omitempty"`
    Security *struct {
        Reason        string        `json:"reason"`
        InjectDropped string        `json:"inject_dropped"`
        InjectSkipped []interface{} `json:"inject_skipped,omitempty"`
    } `json:"security,omitempty"`
}

For command-line analysis, filter denied requests with jq:

grep '"decision":"deny"' /data/log/cube-egress/access.jsonl | jq .

You can also write custom security events from Lua:

local audit = require "audit"
audit.bootstrap({file_path = "/data/log/cube-egress/access.jsonl"})
audit.write_security_event("secret_not_found", {
    policy_id = "sandbox-123",
    rule_id   = "rule-abc",
    allow     = false,
    injected_headers = {}
})

Summary

  • CubeEgress writes comprehensive audit records to /data/log/cube-egress/access.jsonl in JSONL format via CubeEgress/lua/audit.lua.
  • Each record includes temporal data, unique request IDs, sandbox identity (src_ip, policy_id), connection details (original_dst_ip, original_dst_port), TLS metadata, and HTTP metrics.
  • The policy field captures the matched rule, decision (allow/deny/unknown), and latency in microseconds.
  • Security events and credential injection metadata are logged without exposing secret values, with dedicated record types for TLS failures and early security events.
  • Writes are atomic and safe for concurrent access due to the PIPE_BUF size constraint and cjson.safe encoding.

Frequently Asked Questions

Where are the audit logs stored?

By default, CubeEgress writes audit logs to /data/log/cube-egress/access.jsonl. This path is configured in CubeEgress/lua/audit.lua via the FILE_PATH variable and is ensured to exist by the CubeEgress/start.sh initialization script.

What sensitive data is redacted from the logs?

The credentials field logs only the names of injected headers and their corresponding secret IDs, never the actual secret values. Additionally, the fields redacted_request_headers, redacted_request_body, and redacted_response_body are present but always set to null in current implementations, reserved for future "full-audit" modes that would apply redaction rules from the helper redactor module.

How are unique request IDs generated?

The request_id field prioritizes ngx.var.request_id when available. If the nginx variable is not present, the system generates an identifier by combining the connection ID with an internal request counter, ensuring unique tracking within each worker process.

Can I write custom security events from my own Lua code?

Yes. The CubeEgress/lua/audit.lua module exposes _M.write_security_event() for external use. You must first call audit.bootstrap() with the target file path, then invoke write_security_event() with the reason string and relevant metadata to emit a dedicated security event record that persists even if the standard HTTP request logging fails.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →