Keep session evidence without a live connection

Record .NET app behavior and connectivity without a live host, then export, import, and review the captured evidence.

You can preserve the session where an issue occurs even when the app is not connected to an Ansight host. Ansight.OfflineCapture stores telemetry, events, touches, screenshots, network requests, opt-in connectivity observations, and annotated feedback locally for later review or handoff.

It supports compact local storage, retention, runtime option changes, ZIP export, optional AES-256 encryption, and annotated feedback bundles. Recording, export, and local review require no Ansight account. Team upload is an optional, separate cloud integration.

The package is referenced by Ansight and Ansight.Maui, but capture does not start until app code configures an OfflineCaptureController, initializes it, and starts capture manually or through an activation mode. This page describes the managed .NET workflow available in SDK 1.8.0. For the native recording and reconnect-transfer implementation added after 1.8.0 across all mobile SDKs, see Native Offline Capture. Do not run this controller alongside the native recorder.

Configure and Start

using Ansight;
using Ansight.Connectivity;
using Ansight.OfflineCapture;

// Add connectivity to the app's existing Ansight configuration.
var options = Options.CreateBuilder()
    .WithConnectivityCapture()
    .Build();
Runtime.InitializeAndActivate(options);

var offlineCapture = OfflineCapture.Configure(new OfflineCaptureOptions
{
    MaximumSessionBytes = 128 * 1024 * 1024,
    RetentionWindowOverride = TimeSpan.FromMinutes(10)
});

await offlineCapture.InitializeAsync();
await offlineCapture.StartAsync();

Initialize the runtime once, then initialize the offline controller. If your app already initializes Ansight through UseAnsight or another setup path, add WithConnectivityCapture() there and reuse that runtime. No host connection is required to start recording.

Leaving RootDirectory unset stores captures under Ansight/.ansight in the platform’s local application-data directory, falling back to the application base directory. Set it explicitly to use another app-writable location. The example keeps a ten-minute rolling window; choose the duration needed for the reproduction.

If the app does not reference an all-in-one package, install the workflow directly:

dotnet add package Ansight.OfflineCapture
dotnet add package Ansight.Connectivity

Ansight.Connectivity is needed for the optional connection observer in this example. See Connectivity Capture for sampling and platform support.

Offline capture uses the runtime retention period and SessionJpegCapture settings by default. The all-in-one preset retains 120 seconds unless you override it; a byte limit alone does not extend that time window. Change only the offline behavior with runtime overrides:

await offlineCapture.UpdateOptionsAsync(options =>
{
    options.RetentionWindowOverride = TimeSpan.FromSeconds(30);
    options.SessionJpegCaptureEnabledOverride = false;
});

ActivationMode, retention limits, segment duration, and session-JPEG overrides can change while capture is active. RootDirectory and MaximumQueuedRecords affect writer ownership and queue construction, so stop capture before changing either.

Activation Modes

ModeBehavior
DisabledDo not start automatically.
ImmediateStart now and persist Disabled for future sessions.
NextSessionOnlyPersist a one-shot future start. Initialization starts the capture and clears the persisted mode without stopping the active session.
AlwaysOnStart capture for every app session until disabled.

Captured Evidence

The controller stores:

  • channel, device-profile, and custom-property metadata
  • metrics and events
  • touch input
  • sanitized HTTP request records from enabled network capture integrations
  • connection snapshots, changes, quality samples, and gaps when Connectivity Capture is enabled
  • periodic JPEG screenshots and their index
  • recovered native crash reports and bounded OS traces when crash attachment is enabled
  • .ansightannotation feedback bundles when Annotated Feedback is enabled

Data is written as compact JSONL under .ansight/sessions/{sessionId}:

.ansight/
  settings.json
  sessions/
    {sessionId}/
      manifest.json
      metadata/
        channels.json
        device-profile.json
        custom-properties.json
      telemetry/
        metrics/
          m-{utc}.jsonl
        events/
          e-{utc}.jsonl
      input/
        touches/
          t-{utc}.jsonl
      network/
        requests/
          {utc}-{requestId}.json
      connectivity/
        index.json
        segments/
          000001.jsonl
      screenshots/
        {utc}.jpg
        index/
          s-{utc}.jsonl
      annotations/
        bundles/
          {annotationId}.ansightannotation
        index.jsonl
      diagnostics/
        crashes/
          {crashReportId}.json
          {crashReportId}.trace

Annotation writes are isolated from the bounded telemetry queue, so telemetry backpressure does not drop a submitted feedback bundle.

Connectivity recording includes the last known observation at capture start with its original timestamp. Segmented files and retention preserve preceding state for replay. Background pauses and dropped connectivity records produce explicit gaps; the manifest’s DroppedRecordCount reports queue loss. No connectivity index is written when no observations were captured.

Offline network records retain the sanitized evidence supplied by the runtime, including bodies when the network capture policy allows them. Configure body capture and redaction before recording.

If the process terminates while capture is active, native Crash Capture recovers the report on the next launch and correlates it with the offline manifest’s ProcessSessionId. With CrashCaptureOptions.OfflineCaptureAttachmentEnabled enabled, the normalized report and bounded trace are written under diagnostics/crashes; the manifest records StoppedAtUtc, TerminationKind, and CrashReportIds. ZIP export and team upload include these files automatically. A normal StopAsync() seals the manifest with normal termination.

Retention

Retention runs during startup, active writes, runtime option updates, and export preparation:

  • Time retention deletes closed files older than the effective retention window.
  • Active writer files are never deleted.
  • MaximumSessionBytes trims old closed files inside the active session.
  • MaximumRetainedBytes trims old closed files across the .ansight root.

Export

Stop capture when the exported archive must be immutable:

await offlineCapture.StopAsync();

await offlineCapture.ExportToFileAsync(
    "capture.zip",
    new OfflineCaptureExportOptions
    {
        Password = "optional-password"
    });

The SDK supports:

  • ExportToFileAsync(path, options) for a ZIP file
  • ExportToStreamAsync(stream, options) for a caller-owned stream
  • OfflineCaptureExportOptions.Password for AES-256 entry encryption on current net9.0 targets
  • unencrypted export through System.IO.Compression.ZipArchive

ZIP exports stream the raw capture files. The resident host ingests this compact format directly; export does not expand JSONL records into a separate archive JSON document.

Import and Review

Transfer capture.zip from the app’s writable storage to the developer machine using your app’s export or file-sharing flow. Start the host with ansight host run --open, then run these commands in another terminal:

ansight session import capture.zip --json
ansight serve --open --session <session-id>
ansight session connectivity <session-id> summary --json
ansight session connectivity <session-id> at --offset 01:15 --json

Replace <session-id> with the ID returned by import. For a password-protected ZIP, set ANSIGHT_CAPTURE_PASSWORD in your environment and use:

ansight session import capture.zip --password-env ANSIGHT_CAPTURE_PASSWORD --json

The imported session includes its retained connectivity, screenshots, logs, network records, crash evidence, and annotation bundles. Open Connectivity in the player to compare a connection change with the rest of the evidence. See Connectivity Review for time windows, quality charts, and agent queries.

Team Upload

Team upload requires an explicit reference to the separate private Ansight.Cloud.OfflineCapture integration alongside Ansight.OfflineCapture. That assembly supplies the UploadAsync(...) extension and upload option types in the Ansight.OfflineCapture namespace. The public offline-capture package contains no uploader or hosted endpoint.

Team admins and owners can issue an app-scoped capture API key from the Ansight portal. The secret is shown once and can be revoked without changing the app’s connection profile.

Stop the capture before upload:

await offlineCapture.StopAsync();

var result = await offlineCapture.UploadAsync(
    new OfflineCaptureUploadOptions
    {
        ApiKey = Environment.GetEnvironmentVariable("ANSIGHT_CAPTURE_API_KEY")!,
        Title = "Checkout regression"
    },
    new Progress<OfflineCaptureUploadProgress>(update =>
    {
        Console.WriteLine(
            $"{update.Stage}: {update.BytesTransferred}/{update.TotalBytes}");
    }));

Console.WriteLine(result.SessionUrl);

OfflineCaptureUploadOptions.Endpoint defaults to the hosted Ansight ingest function and can be overridden for local development or self-hosting. The uploader:

  • requests a one-archive signed storage URL without sending the app key to object storage
  • verifies that the capture manifest app/package id matches the key’s app
  • retries transient API and storage failures with an idempotency key
  • removes the temporary ZIP after completion or failure

Offline screenshots add rendering and encoding work even without a network connection. Disable SessionJpegCapture or set SessionJpegCaptureEnabledOverride = false for trend-focused runs unless visual evidence is required.

Captures may contain rendered user data, logs, object state, paths, crash messages and traces, and annotated feedback. Apply suitable retention, encryption, upload-key handling, and app-level consent for the environment. Disable OfflineCaptureAttachmentEnabled when crash evidence must not be attached to offline sessions.