Audit Trail

Every session and file-transfer event is recorded by the audit subsystem. Four audit sinks can run simultaneously — each captures the same events independently. Enable any combination to match your compliance and operational monitoring requirements.

Audit Sinks

All audit sinks are configured inside the <audit> element of sftp-server.xml. Each sink has an enabled attribute that can be toggled without removing the configuration block.

1. Log Sink

The log sink writes audit events to the SLF4J logger com.ivitechnologies.stylussftpserver.audit at INFO level. It is enabled by default and requires no additional configuration.

<log enabled="true"/>

To route audit log entries to a separate file, add a dedicated Log4j 2 appender in log4j2.xml that targets the com.ivitechnologies.stylussftpserver.audit logger:

<Logger name="com.ivitechnologies.stylussftpserver.audit"
        level="INFO" additivity="false">
    <AppenderRef ref="AuditFile"/>
</Logger>
Tip Use a RollingFileAppender with size- or time-based rotation to prevent audit log files from growing indefinitely.

2. JDBC Sink

The JDBC sink writes events asynchronously to the sftp_audit database table via a LinkedBlockingQueue with a configurable capacity (default 10,000). Events are batched for throughput. The table is auto-created on first start.

<jdbc enabled="true">
    <driver-class-name>org.h2.Driver</driver-class-name>
    <url>jdbc:h2:${STYLUS_SFTPSERVER_DATA}/db/sftpdb;AUTO_SERVER=TRUE</url>
    <username>sa</username>
    <password>secret</password>
    <max-pool-size>5</max-pool-size>
    <queue-capacity>10000</queue-capacity>
</jdbc>
Element Description Default
<driver-class-name> Fully qualified JDBC driver class. org.h2.Driver
<url> JDBC connection URL. Supports ${STYLUS_SFTPSERVER_DATA} macro.
<username> Database username. sa
<password> Database password.
<max-pool-size> Maximum HikariCP connection pool size. 5
<queue-capacity> Maximum number of events buffered before blocking. 10000
Note The JDBC sink is required for several features that depend on stored audit data: email notifications, the admin audit viewer, and session tracking. If you disable it, those features will not function.

3. Syslog Sink (RFC 5424 UDP)

The syslog sink sends fire-and-forget UDP packets to a central syslog collector such as Splunk, ELK, or QRadar. Overhead is near-zero (approximately 10 microseconds per event).

<syslog enabled="true">
    <host>siem.example.com</host>
    <port>514</port>
    <facility>LOCAL0</facility>
</syslog>
Element Description Default
<host> Hostname or IP address of the syslog collector.
<port> UDP port number. 514
<facility> Syslog facility: LOCAL0 through LOCAL7. LOCAL0

Each message follows RFC 5424 format with structured data in a [sss@0] SD element containing all audit record fields. Severity mapping:

Tip If the syslog collector is unreachable, UDP packets are silently dropped. The JDBC sink remains the authoritative audit record — syslog is a best-effort complement for real-time monitoring.

4. Webhook Sink

The webhook sink sends fire-and-forget HTTP POST requests via the JDK HttpClient. You can configure multiple webhooks, each with its own URL, event filter, and output format.

<webhooks>
    <webhook enabled="true">
        <url>https://hooks.slack.com/services/T00/B00/xxx</url>
        <events>UPLOAD_COMPLETE, AUTH_FAILURE</events>
        <format>text</format>
        <template>SSS ${event}: ${filename} by ${username}</template>
    </webhook>
</webhooks>

Formats

Format Content-Type Description
json application/json Structured JSON object with all event fields. Suitable for APIs and automation pipelines.
text application/json Template-rendered message wrapped in {"text":"..."}. Suitable for Slack and Microsoft Teams incoming webhooks.

Template Variables

Variable Description
${event} Event type (e.g., UPLOAD_COMPLETE)
${timestamp} ISO 8601 timestamp
${username} Authenticated username
${remoteIp} Client IP address
${filename} File path (uploads and downloads)
${bytes} File size in bytes
${success} true or false
${detail} Additional detail message

HMAC Signing

To verify that webhook payloads originate from Stylus SFTP Server, configure a shared secret. The server computes an HMAC-SHA256 signature over the request body and sends it in the X-SSS-Signature HTTP header. The receiving endpoint can recompute the HMAC and compare.

<webhook enabled="true">
    <url>https://api.example.com/sftp-events</url>
    <events>UPLOAD_COMPLETE, DOWNLOAD_COMPLETE</events>
    <format>json</format>
    <secret>my-shared-secret</secret>
</webhook>

Event Types

The table below lists the protocol-triggered event types — authentication, transfer, session, MFA, quota. Admin-triggered events (org / group / folder mutations, license administration, auth-orchestrator diagnostics) also flow through the audit stream but are catalogued exhaustively in Appendix B, which stays authoritative as new events are added.

Event Description Severity
AUTH_SUCCESS Successful authentication INFO
AUTH_FAILURE Failed authentication attempt WARNING
AUTH_ACCOUNT_LOCKED Account locked after consecutive failure threshold WARNING
SESSION_CONNECT Client connected INFO
SESSION_CLOSED Client disconnected INFO
UPLOAD_COMPLETE File upload committed (staging renamed to final) INFO
UPLOAD_ABORTED Upload cancelled by client WARNING
UPLOAD_REJECTED Upload denied (quota exceeded, read-only access, or error) WARNING
DOWNLOAD_COMPLETE File download completed INFO
LICENSE_REJECTED Invalid, expired, or missing activation key WARNING
MFA_SUCCESS TOTP verification succeeded INFO
MFA_FAILURE TOTP verification failed (wrong code) WARNING
MFA_ENROLLED User completed MFA enrollment INFO
MFA_DISABLED MFA disabled for a user (by admin or self-service) INFO
MFA_RECOVERY_USED A one-time recovery code was consumed during MFA verification INFO
USER_CREATED An SFTP end-user account was created by an administrator INFO
USER_UPDATED An SFTP end-user property changed (enable/disable, password, home path, quota, email, read-only flag, public keys, unlock). The detail column identifies which property changed. INFO
USER_DELETED An SFTP end-user account was deleted by an administrator INFO
ADMIN_USER_CREATED An admin-console operator account was created INFO
ADMIN_USER_UPDATED An admin-console operator property changed (password, email, enable/disable) INFO
ADMIN_USER_DELETED An admin-console operator account was deleted INFO
ORG_CREATED, ORG_UPDATED, ORG_DELETED An organization was created, edited, or removed. The username column carries the org's name. INFO
GROUP_CREATED, GROUP_UPDATED, GROUP_DELETED A group was created, edited, or removed. The username column carries the group's name; detail names the parent organization. INFO
FOLDER_CREATED, FOLDER_UPDATED, FOLDER_DELETED A virtual folder was created, edited, or removed. The filename column carries the logical folder path. INFO
MEMBERSHIP_ADDED, MEMBERSHIP_REMOVED A user was added to or removed from a group. The username column identifies the affected user; detail names the group and org. INFO

Audit Record Fields

Every audit event carries the same set of fields, regardless of which sink receives it:

Field Type Description
Event type String One of the event types listed above.
Timestamp Timestamp When the event occurred, stored as UTC. The Web and Swing admin surfaces render it in the operator's local timezone; JDBC / syslog / webhook sinks emit UTC.
Executed by String Admin username that triggered the mutation, or null for protocol-triggered events (uploads, downloads, session events). Populated by the Web filter, Swing frame, and CLI when they call AdminEngine mutations.
Username String The authenticated username, or null for pre-auth events.
Remote IP String Client IP address (IPv4 or IPv6, up to 45 characters).
Filename String File path for upload/download events.
File size Long Size in bytes for upload/download events.
Success Boolean true for successful operations, false for failures.
Detail String Additional context (e.g., failure reason, quota detail).

sftp_audit Table Schema

When the JDBC sink is enabled, the sftp_audit table is auto-created on first start with the following schema:

CREATE TABLE sftp_audit (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    occurred_at TIMESTAMP      NOT NULL,
    event_type  VARCHAR(32)    NOT NULL,
    username    VARCHAR(255),
    remote_ip   VARCHAR(45),
    filename    VARCHAR(1000),
    bytes       BIGINT,
    success     SMALLINT       NOT NULL,
    detail      VARCHAR(2000),
    executed_by VARCHAR(255)
)

CREATE TABLE sftp_audit_to_organization (
    audit_id   BIGINT    NOT NULL,
    org_id     BIGINT    NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT pk_sftp_audit_to_organization PRIMARY KEY (audit_id, org_id),
    CONSTRAINT fk_sftp_audit_to_organization_org
        FOREIGN KEY (org_id) REFERENCES sftp_organizations (org_id) ON DELETE CASCADE
)

All timestamps are stored as UTC. The application supplies each occurred_at value as a UTC instant when the event is recorded, and the install-time service registration sets -Duser.timezone=UTC so the JVM agrees; the admin surfaces convert to the operator's local timezone at display time.

The sftp_audit_to_organization link table records which organizations are relevant to each event. A single event can be tagged with multiple organizations — a Force5-employed operator acting on Austin Energy's folder produces an event linked to both orgs, so the Audit view can surface it under either tenant's filter. Pre-identity-model rows have no links and surface only in unfiltered queries.

Column Type Description
id BIGINT IDENTITY Auto-generated surrogate primary key.
event_type VARCHAR(32) Event type code (e.g., AUTH_SUCCESS).
occurred_at TIMESTAMP Event timestamp (UTC), supplied by the application when the event is recorded.
username VARCHAR(255) Authenticated username. Nullable for pre-auth events.
remote_ip VARCHAR(45) Client IP address (supports full IPv6 notation).
filename VARCHAR(1000) File path for file-transfer events.
bytes BIGINT File size in bytes. Nullable.
success SMALLINT 1 = success, 0 = failure.
detail VARCHAR(2000) Free-text detail message. Nullable.
Tip Indexes on occurred_at, event_type, and username are created automatically, so audit-viewer queries stay fast even on high-volume servers.