Logging Strategy

June 17, 2026 · View on GitHub

Vision

  • Emit structured, security-focused telemetry for every critical SSH interaction, using JSON as the canonical event payload.
  • Support multiple transports (stdout by default, optional syslog) without forcing runtime restarts or code changes.
  • Preserve privacy by masking secrets, dropping high-volume clutter, and normalising metadata.

Guiding Principles

  • Prefer pure data assembly functions that return Result objects so failures are observable and non-fatal.
  • Keep modules small and transport-agnostic; compose via dependency injection.
  • Never log credentials, private key material, or raw terminal content.
  • Default to UTC ISO-8601 timestamps and stable field keys.
  • Reject events that exceed size thresholds or include unsafe characters for downstream systems.

Event Model

Each log event combines core metadata with an event-specific payload. Core fields:

FieldTypeNotes
tsstringRFC 3339 UTC timestamp.
leveldebug|info|warn|errorInclusive severity ladder.
eventstringOne of the catalogued event identifiers.
session_idstringBranded session identifier from the session store.
request_idstringCorrelates HTTP request, socket, and downstream actions.
client_ipstringResolved client IP (trusting configured proxy chain).
client_portnumberClient source port when available.
client_source_portnumberBacking TCP port observed from the socket connection (post-proxy).
user_agentstringNormalised browser agent string (logged on session_init only).
usernamestringNormalised username when known.
target_hoststringFinal SSH target hostname or IP.
target_portnumberFinal SSH target port.
protocolstringssh, sftp, or scp once supported.
subsystemstringshell, sftp, scp, or exec.
statusstringsuccess or failure semantics per event.
duration_msnumberMilliseconds captured via monotonic timers.
bytes_in/bytes_outnumberAggregate stream counters when available.

Event Catalog

Authentication Events

EventDescription
auth_attemptUser initiated authentication (password or key)
auth_successAuthentication completed successfully
auth_failureAuthentication rejected by SSH server (wrong credentials)
credential_replayStored credentials reused for reconnection

Connection Events

EventDescription
connection_failureConnection failed before authentication (network, algorithm mismatch, timeout)

The connection_failure event differs from auth_failure:

  • auth_failure: SSH server rejected credentials (user error)
  • connection_failure: Connection could not be established (infrastructure/config issue)

The connection_failure event includes an errorType field with one of:

  • algorithm: Server and client have no compatible algorithms
  • network: Network unreachable, connection refused, DNS failure
  • timeout: Connection or handshake timed out
  • unknown: Other unexpected failures

Session Events

EventDescription
session_initWebSocket connection established, session created
session_startSSH shell or subsystem opened
session_endSession terminated (user logout, disconnect, timeout)

Terminal Events

EventDescription
ssh_commandShell command execution via exec channel
pty_resizeTerminal dimensions changed
idle_timeoutSession terminated due to inactivity

Policy Events

EventDescription
policy_blockAction denied by security policy
csp_violationContent-Security-Policy violation report received at /ssh/csp-report (report-only or enforce mode); rate-limited; the expected legacy-inline-script violation is demoted to debug during the deprecation window
errorUnexpected system failure (include error_code and reason)

SFTP Events

EventDescription
sftp_listDirectory listing requested
sftp_statFile metadata requested
sftp_mkdirDirectory created
sftp_deleteFile or directory deleted
sftp_upload_startFile upload initiated
sftp_upload_chunkUpload chunk transferred
sftp_upload_completeUpload finished successfully
sftp_upload_cancelUpload cancelled by user or error
sftp_download_startFile download initiated
sftp_download_chunkDownload chunk transferred
sftp_download_completeDownload finished successfully
sftp_download_cancelDownload cancelled by user or error

Prompt Events

EventDescription
prompt_sentInteractive prompt sent to user
prompt_responseUser responded to prompt
prompt_timeoutPrompt timed out waiting for response
prompt_errorError processing prompt

Log Levels

LevelUsage
errorUnexpected system failures, bugs, unrecoverable conditions
warnExpected failure conditions (wrong password, config mismatch, recoverable errors)
infoNormal operations (session start, auth success, significant state changes)
debugDetailed tracing, diagnostic information, verbose operational data

Event payloads should be defined in TypeScript using discriminated unions so the compiler enforces completeness.

Transport Architecture

  1. Stdout transport (default)

    • Writes newline-delimited JSON to process.stdout.
    • Honors log level filtering per configuration.
    • Resilient to partial writes by buffering in memory and draining on drain events.
  2. Syslog transport (optional)

    • Implements RFC 5424 structured data, with TLS per RFC 5425 and octet-counted framing per RFC 6587.
    • Adds structured data block [webssh2@<enterprise-id> ...] with high-signal attributes.
    • Reconnects with exponential backoff and surfaces health via metrics.

Transports must be pluggable; attaching or detaching one cannot impact the core log formatting pipeline.

Configuration Surface

Environment variables (all prefixed WEBSSH2_LOGGING_):

  • LEVEL: minimum severity to emit (info default).
  • STDOUT_ENABLED: enable/disable stdout delivery (defaults to true).
  • STDOUT_MIN_LEVEL: override for stdout transport only.
  • SYSLOG_ENABLED: enable RFC 5424 forwarding (defaults to false).
  • SYSLOG_HOST, SYSLOG_PORT, SYSLOG_APP_NAME, SYSLOG_ENTERPRISE_ID: syslog endpoint metadata.
  • SYSLOG_TLS_ENABLED, SYSLOG_TLS_CA_FILE, SYSLOG_TLS_CERT_FILE, SYSLOG_TLS_KEY_FILE, SYSLOG_TLS_REJECT_UNAUTHORIZED: TLS configuration.
  • SYSLOG_BUFFER_SIZE, SYSLOG_FLUSH_INTERVAL_MS: client-side buffering controls.
  • SYSLOG_INCLUDE_JSON: append the JSON payload after the structured data block when true.

Configuration is parsed through the existing env-mapper, validated via zod schemas, and surfaced through the DI container.

Runtime Controls (Phase 2)

Structured logging can now be tuned at runtime through logging entries in the primary config (config.json or environment overrides):

  • logging.minimumLevel: adjusts the inclusive severity threshold across every structured logger.
  • logging.controls.sampling: accepts a defaultSampleRate and optional rules[] keyed by event name (or *) to drop high-volume events deterministically.
  • logging.controls.rateLimit: provides rules[] with limit, intervalMs, and optional burst tokens for per-event or wildcard throttling.

Environment overrides keep these controls in sync without touching config.json:

  • WEBSSH2_LOGGING_LEVELlogging.minimumLevel
  • WEBSSH2_LOGGING_STDOUT_ENABLEDlogging.stdout.enabled
  • WEBSSH2_LOGGING_STDOUT_MIN_LEVELlogging.stdout.minimumLevel
  • WEBSSH2_LOGGING_SAMPLING_DEFAULT_RATElogging.controls.sampling.defaultSampleRate
  • WEBSSH2_LOGGING_SAMPLING_RULESlogging.controls.sampling.rules (JSON array)
  • WEBSSH2_LOGGING_RATE_LIMIT_RULESlogging.controls.rateLimit.rules (JSON array)
  • WEBSSH2_LOGGING_SYSLOG_ENABLEDlogging.syslog.enabled
  • WEBSSH2_LOGGING_SYSLOG_HOST / WEBSSH2_LOGGING_SYSLOG_PORTlogging.syslog.host / port
  • WEBSSH2_LOGGING_SYSLOG_APP_NAMElogging.syslog.appName
  • WEBSSH2_LOGGING_SYSLOG_ENTERPRISE_IDlogging.syslog.enterpriseId
  • WEBSSH2_LOGGING_SYSLOG_BUFFER_SIZElogging.syslog.bufferSize
  • WEBSSH2_LOGGING_SYSLOG_FLUSH_INTERVAL_MSlogging.syslog.flushIntervalMs
  • WEBSSH2_LOGGING_SYSLOG_INCLUDE_JSONlogging.syslog.includeJson
  • WEBSSH2_LOGGING_SYSLOG_TLS_*logging.syslog.tls.*

Transports

  • Stdout (default)
    • Bounded queue (default 1000 events) with drain awareness.
    • Optional per-transport minimum level via WEBSSH2_LOGGING_STDOUT_MIN_LEVEL.
  • Syslog (optional)
    • RFC 5424 serializer with deterministic structured data under webssh2@<enterpriseId>.
    • Octet-counted framing over TCP with automatic reconnect and exponential backoff.
    • Optional TLS (RFC 5425) with filesystem-backed credentials and toggleable certificate validation.
    • Dual-delivery supported; enable both stdout and syslog when operating in hybrid environments.

StructuredLogger#snapshotMetrics() exposes counters for published, sampled, rate-limited, and queue-dropped events to aid operational dashboards.

Syslog Forwarding

Enabling syslog only requires host and port:

WEBSSH2_LOGGING_SYSLOG_ENABLED=true \
WEBSSH2_LOGGING_SYSLOG_HOST=syslog.example.com \
WEBSSH2_LOGGING_SYSLOG_PORT=6514 \
WEBSSH2_LOGGING_SYSLOG_TLS_ENABLED=true \
WEBSSH2_LOGGING_SYSLOG_TLS_CA_FILE=/etc/pki/ca.pem

Key behaviours:

  • Severity is derived from the structured level and emitted under facility local0 by default.
  • Structured data includes event metadata (session_id, username, client_ip, connection_id).
  • When WEBSSH2_LOGGING_SYSLOG_INCLUDE_JSON=true, the JSON payload is appended after the structured data block for downstream enrichment.
  • Buffered queue (WEBSSH2_LOGGING_SYSLOG_BUFFER_SIZE) and flush cadence (WEBSSH2_LOGGING_SYSLOG_FLUSH_INTERVAL_MS) guard against slow collectors; if the queue is exhausted the logger records a TransportBackpressureError and drops new events non-fatally.
  • TLS credentials are read during transport initialisation; invalid paths leave syslog disabled with a warning on stderr.

Implementation Milestones

  1. Replace the current Logger interface with a structured logger accepting { level, event, data }.
  2. Implement shared utilities for timestamps, correlation IDs, byte counters, and error serialisation (Result based).
  3. Instrument authentication, session, and SSH services to emit events with exhaustive metadata. Capture client network fingerprint (IP, port, user agent) during the initial socket handshake.
  4. Layer in syslog transport once stdout coverage is complete and validated.
  5. Add integration tests that assert log production for representative socket flows.

Testing Expectations

  • Unit tests for formatter correctness, field validation, and masking logic.
  • Integration tests (Vitest) that exercise authentication and command execution flows, asserting emitted events against Golden JSON snapshots.
  • Playwright smoke tests ensuring critical user actions result in log output.
  • Chaos tests (optional) simulating syslog disconnects to confirm buffered resend.

Operational Guidance

  • stdout logs are container-friendly and should be shipped via the hosting platform.
  • Syslog transport is opt-in; operators must provision certificates and network routes.
  • Document the event schema in release notes so SIEM parsers stay in sync.
  • Provide scripts for validating syslog connectivity (e.g., openssl s_client).

Current Progress

  • app/logging/ contains the structured formatter, log level helpers, stdout transport, and application-facing logger factory.
  • app/logger.ts exposes createAppStructuredLogger and now emits structured error events in addition to legacy console output.
  • Socket authentication/terminal adapters publish structured events for auth attempts, successes/failures, exec activity, and capture client network metadata for downstream events.
  • Unit coverage lives under tests/unit/logging/, validating formatting, level filtering, socket context derivation, and stdout backpressure handling.

Future Enhancements

  • Support OpenTelemetry log record export once Node SDK stabilises.
  • Introduce demand-based sampling for high-frequency events (e.g., pty_resize, sftp_*_chunk).
  • Extend event catalog as new features (SCP subsystem) ship.
  • Publish schema versioning to allow consumers to detect breaking changes.