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
| 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
- 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
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.
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. 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.