Offline Capture

Record .NET telemetry, events, touches, screenshots, and annotated feedback without a Studio connection, then retain, export, encrypt, or upload the capture.

Ansight.OfflineCapture records session evidence without requiring a live Studio connection. It supports compact local storage, retention, runtime option changes, ZIP export, optional AES-256 encryption, annotated feedback bundles, and team upload.

The package is referenced by Ansight and Ansight.Maui, but capture does not start until app code configures and initializes an OfflineCaptureController.

Configure and Start

using Ansight.OfflineCapture;

var offlineCapture = OfflineCapture.Configure(new OfflineCaptureOptions
{
    RootDirectory = ".ansight",
    MaximumSessionBytes = 128 * 1024 * 1024
});

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

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

dotnet add package Ansight.OfflineCapture --prerelease

Offline capture uses the runtime retention period and SessionJpegCapture settings by default. 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
  • 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
      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.

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. Studio ingests this compact format directly; export does not expand JSONL records into a Studio archive JSON document.

Team Upload

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

Performance and Privacy

Offline screenshots add rendering and encoding work even without a network connection. Disable SessionJpegCapture or set SessionJpegCaptureEnabledOverride = false for performance-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.