ToolUp.AuditSinks.SplunkHec 0.23.0

Prefix Reserved
dotnet add package ToolUp.AuditSinks.SplunkHec --version 0.23.0
                    
NuGet\Install-Package ToolUp.AuditSinks.SplunkHec -Version 0.23.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ToolUp.AuditSinks.SplunkHec" Version="0.23.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ToolUp.AuditSinks.SplunkHec" Version="0.23.0" />
                    
Directory.Packages.props
<PackageReference Include="ToolUp.AuditSinks.SplunkHec" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ToolUp.AuditSinks.SplunkHec --version 0.23.0
                    
#r "nuget: ToolUp.AuditSinks.SplunkHec, 0.23.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ToolUp.AuditSinks.SplunkHec@0.23.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ToolUp.AuditSinks.SplunkHec&version=0.23.0
                    
Install as a Cake Addin
#tool nuget:?package=ToolUp.AuditSinks.SplunkHec&version=0.23.0
                    
Install as a Cake Tool

Splunk HEC audit sink

Phase 9g IAuditSink companion. POSTs every audit batch to a Splunk HTTP Event Collector endpoint with an HEC token from ISecretStore. No Splunk SDK dependency — uses BCL HttpClient directly.

How to enable

  1. Reference this companion's Server.props in the consuming server project's .fsproj and add a <ProjectReference> to its .fsproj.

  2. Provision a Splunk HEC token (Splunk admin UI → Settings → Data Inputs → HTTP Event Collector → New Token). Note the token value.

  3. Store the token in ISecretStore under the _platform scope:

    do! secretStore.SetSecret("_platform", "splunk_hec_token", "abc123-def456-...")
    

    Or load it from environment variables via EnvironmentSecretStore — TOOLUP_SECRET_PLATFORM_SPLUNK_HEC_TOKEN=abc123....

  4. Construct the sink and register:

    open ToolUp.Platform.AuditSinks.SplunkHec
    open System.Net.Http
    
    let httpClient = new HttpClient()
    let settings: SplunkHecSettings = {
        EndpointUrl = "https://splunk.example.com:8088/services/collector/event"
        Sourcetype = "toolup_audit"
        Index = Some "audit"
        Host = Some "toolup-prod"
    }
    let sink = SplunkHec.create "splunk-prod" settings secretStore "splunk_hec_token" httpClient
    
    ServerApp.empty
    |> ServerApp.withAuditSink sink
    |> ServerApp.run
    

Wire format

Splunk HEC's /services/collector/event endpoint accepts newline-delimited JSON. Each line:

{"event":{"Case":"UserLoggedIn","Fields":[{"UserId":"u123","AuthProvider":"Header"}]},"sourcetype":"toolup_audit","_meta":{"uuid":"...","event_type":"UserLoggedIn"},"index":"audit","host":"toolup-prod"}
  • event — the SDK's AuditEvent JSON, serialised via FableJsonConverter (the SDK's canonical converter). Splunk's spath extracts fields directly; SPL queries like index=audit "Case"="UserLoggedIn" Fields{}.UserId="u123" work without further processing.
  • sourcetype — toolup_audit by default. Splunk admins use this to route audit events to a dedicated index, dashboards, and alerts without needing to inspect the payload.
  • _meta.uuid — random GUID per event for Splunk-side deduplication on retry. The dispatcher retries entire batches on Result.Error, so an event may be POSTed multiple times if Splunk transiently fails after accepting some events but before completing the response. Splunk's _meta.uuid is the dedup key.
  • _meta.event_type — wire-format event-type name (mirrors AuditEvent.eventTypeName). Splunk indexes this as a top-level field for fast type filtering.
  • index / host — optional pins. Configure at the SDK side or rely on the HEC token's defaults.

Authentication + token rotation

Authorization: Splunk <token> header. The token is read from ISecretStore on every Deliver call — no caching. Rotated tokens flow through immediately (next batch picks up the new value); stale tokens fail with Result.Error after Splunk returns 401, and the dispatcher exhausts retries.

Status code handling

Splunk response Sink result Dispatcher behaviour
200 OK Ok () Cursor advances
4xx Bad Request Error (HTTP 4xx body) Retried per RetryPolicy; if exhausted, dead-lettered (operators investigate token / format)
5xx Server Error Error (HTTP 5xx body) Retried per RetryPolicy (transient infra)
Connection refused / DNS fail Error (exception message) Retried per RetryPolicy

The sink doesn't distinguish 4xx from 5xx at the interface — both surface as Result.Error and the dispatcher's retry policy decides. Tightening this (skipping retries on 4xx) would require the dispatcher to interpret error strings, which would couple the dispatcher to vendor diagnostics.

Acceptance test

A real-Splunk integration test is gated on TOOLUP_SPLUNK_HEC_* env vars (deferred — needs a stable Splunk HEC environment to run against). The unit test against a fake HttpClient handler verifies:

  • POST URL matches the configured endpoint.
  • Authorization: Splunk <token> header populated from ISecretStore.
  • Body is newline-delimited JSON with one line per event.
  • Each line carries event, sourcetype, _meta.uuid, _meta.event_type.

The contract pack (IAuditSinkContract) runs against this companion using a fake HTTP handler that records the POST body — same shape as the SMTP sink's tests against an unreachable host.

Single-instance limitation

Same as every Phase 9g companion — the replicator is in-process. Multi-silo deployments running the same sink double-deliver until Phase 9c half 2's distributed lock lands. Document this in the deployment's compliance posture (or run the audit-emitting tier as replicas: 1 until then).

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.23.0 68 9/23/2026
0.22.0 107 8/27/2026
0.21.0 111 8/26/2026
0.20.1 111 8/20/2026
0.20.0 128 8/19/2026