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
| Mode | Behavior |
|---|---|
Disabled | Do not start automatically. |
Immediate | Start now and persist Disabled for future sessions. |
NextSessionOnly | Persist a one-shot future start. Initialization starts the capture and clears the persisted mode without stopping the active session. |
AlwaysOn | Start 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
.ansightannotationfeedback 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.
MaximumSessionBytestrims old closed files inside the active session.MaximumRetainedBytestrims old closed files across the.ansightroot.
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 fileExportToStreamAsync(stream, options)for a caller-owned streamOfflineCaptureExportOptions.Passwordfor AES-256 entry encryption on currentnet9.0targets- 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
Trends and Privacy
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.