> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Capturing Support Bundles

> Generate on-device diagnostic support bundles with the system_request_support_bundle DQL function, list them in system:support_bundles, and control rate limits, timeouts, and retention.

A support bundle is a tar archive that a device assembles locally. It packages
logs, the effective configuration, and runtime telemetry for analysis by Ditto
Support. Bundles are generated entirely on the device and work offline. No
connection to Ditto Server is required.

> [!NOTE]
>
> Support bundles are available in Ditto SDK 5.2 and later on native platforms.
> The `system_request_support_bundle()` function isn't available in the
> JavaScript Web SDK.

## Requesting a bundle

Request a bundle with the `system_request_support_bundle()` DQL function. Run it
with `EXECUTE FUNCTION`. The call completes when the bundle has been generated
and stored, or fails with an error.

```sql DQL theme={null}
EXECUTE FUNCTION system_request_support_bundle()
```

You can attach your own metadata to the bundle, for example to record why it was
requested. Ditto stores the metadata on the bundle's attachment, and it's
visible when you list bundles:

```sql DQL theme={null}
EXECUTE FUNCTION system_request_support_bundle({
  "metadata": { "requested_by": "support", "ticket": "DIT-1234" }
})
```

The function returns one document that identifies the new bundle:

```json theme={null}
{
  "_id": { "pk": "<peer key>", "created_at": "2026-09-17T19:48:23.776558+00:00" },
  "reason": "USER_INITIATED",
  "file": { "id": "<attachment id>", "len": 31744, "metadata": { "requested_by": "support" } }
}
```

| Field | Description |
| - | - |
| `_id.pk` | The peer key of the device that generated the bundle. |
| `_id.created_at` | The capture time as an RFC 3339 timestamp. |
| `reason` | `USER_INITIATED` for on-demand requests. `CRASH` for bundles captured after a crash. |
| `file` | An [attachment token](https://docs.ditto.live/sdk/latest/crud/working-with-attachments.md#attachment-token) for the bundle archive. `file.metadata` holds any metadata you passed in the request. |

Ditto generates one bundle at a time. If a bundle is already being generated,
Ditto rejects the new request instead of queuing it.

### Requesting from your app

Run the statement with `ditto.store.execute()`, the same way you run any other
DQL statement. Because it's a query, you can trigger it from wherever your app
already runs Ditto queries, such as an admin screen, a remote command from your
backend, or a debug socket.

To download the archive, pass the `file` token to `fetchAttachment()`. See
[Fetching attachments](https://docs.ditto.live/sdk/latest/crud/working-with-attachments.md#fetching-attachments)
for the full fetch lifecycle in each SDK.

```swift Swift theme={null}
let result = try await ditto.store.execute(
    query: "EXECUTE FUNCTION system_request_support_bundle()"
)

if let token = result.items.first?.value["file"] as? [String: Any] {
    let fetcher = try await ditto.store.fetchAttachment(token) { event in
        if case .completed(let attachment) = event {
            // Save the bundle, for example to share it with Ditto Support.
            try? attachment.copy(toPath: "/path/to/support-bundle.tar")
        }
    }
}
```

```kotlin Kotlin theme={null}
ditto.store.execute(
    query = "EXECUTE FUNCTION system_request_support_bundle()",
).use { result ->
    val token = result.items.first().value["file"] as Map<String, Any>

    val fetchResult = ditto.store.fetchAttachment(
        tokenMap = token,
        onFetchProgress = { downloadedBytes, totalBytes -> },
    )

    if (fetchResult is DittoAttachmentFetchResult.Completed) {
        fetchResult.attachment.use { attachment ->
            // Save the bundle, for example to share it with Ditto Support.
            attachment.copyToPath("/path/to/support-bundle.tar")
        }
    }
}
```

```javascript JS theme={null}
const result = await ditto.store.execute(
  "EXECUTE FUNCTION system_request_support_bundle()"
)
const token = result.items[0].value["file"]

const fetcher = await ditto.store.fetchAttachment(token, (event) => {
  if (event.type === "Completed") {
    // Save the bundle, for example to share it with Ditto Support.
    event.attachment.copyToPath("/path/to/support-bundle.tar")
  }
})
```

```csharp C# theme={null}
using var result = await ditto.Store.ExecuteAsync(
    query: "EXECUTE FUNCTION system_request_support_bundle()");

var token = result.Items.First().Value["file"] as Dictionary<string, object>;
var fetcher = ditto.Store.FetchAttachment(token, fetchEvent =>
{
    if (fetchEvent is DittoAttachmentFetchEvent.Completed e)
    {
        // Save the bundle, for example to share it with Ditto Support.
        e.Attachment.CopyToPath("/path/to/support-bundle.tar");
        e.Attachment.Dispose();
    }
});
```

```dart Flutter theme={null}
final result = await ditto.store.execute(
  "EXECUTE FUNCTION system_request_support_bundle()",
);
final doc = result.items.single.value;
final token = Map<String, dynamic>.from(doc["file"] as Map);

final fetcher = ditto.store.fetchAttachment(token, (event) {});
final attachment = await fetcher.attachment;
// Save the bundle, for example to share it with Ditto Support.
await attachment!.copyToPath("/path/to/support-bundle.tar");
```

On Android, the support bundle screen in DittoAndroidTools lets you request,
view, and export bundles without writing queries.

## Listing retained bundles

Ditto stores generated bundles on the device and lists them in the
`system:support_bundles` virtual collection. To list bundles from newest to
oldest, run:

```sql DQL theme={null}
SELECT * FROM system:support_bundles ORDER BY _id.created_at DESC
```

Each document has the same `_id`, `reason`, and `file` fields that
`system_request_support_bundle()` returns. See
[system:support\_bundles](https://docs.ditto.live/dql/virtual-collections.md#system-support-bundles) for
the schema.

## Bundle contents

The archive is a plain `tar` with a flat layout:

| Entry | Contents |
| - | - |
| `ditto-logs.log.gz` | Gzipped export of the on-device Ditto logs (JSON lines), covering the retained log window. |
| `device_logs_METADATA.json` | The time range covered by the log export. |
| `config_snapshot.json` | The effective configuration at capture time: `ditto_config` with secrets redacted, the full system parameter set, `transport_config`, the SDK version, and the capture timestamp. |
| `dial9/dial9.bin.gz` | Tokio runtime telemetry (worker activity over time). Present only when dial9 recording is enabled and at least one 60-second trace segment has completed. |
| `bundle_errors.txt` | Present only when a bundle source failed. Lists the failed sources and their errors. |

> [!NOTE]
>
> Bundles captured in the first 60 seconds of process lifetime don't contain
> dial9 traces. Trace segments rotate every 60 seconds, and only completed
> segments are captured. dial9 recording is on by default at level `Lite`. Change
> it with the `DITTO_DIAL9_OBSERVABILITY_LEVEL` environment variable.

> [!WARNING]
>
> The log export includes a mapping of full peer public keys, so that peer logs
> can be cross-referenced, and absolute on-device file paths. Config secrets are
> redacted from `config_snapshot.json`. Review bundle contents before sharing
> them broadly.

## Rate limiting, timeout, and retention

Three system parameters control bundle generation. Change them at run time with
[`ALTER SYSTEM`](https://docs.ditto.live/dql/alter-system). Changes apply to the next request without a
restart and reset to the defaults when the process restarts. In SDK 5.2 and
later you can also set them in the system parameters of `DittoConfig`.

| Parameter | Default | Range | Behavior |
| - | - | - | - |
| `support_bundle_request_rate` | 3 | 0 or more | Maximum requests admitted per rolling 10-minute window. `0` turns off bundle generation. Rejected requests fail immediately with an error that names the limit and when to retry. The request count survives restarts. |
| `support_bundle_request_timeout` | 30 seconds | 1 to 3600 seconds | Maximum time for bundle collection and archive finalization. A capture that exceeds the deadline fails as a whole. Individual section failures are tolerated and recorded in `bundle_errors.txt`. |
| `support_bundle_retention_limit` | 10 | 1 or more | Maximum bundles retained on the device. Saving beyond the limit evicts the oldest first. |

For example, to allow more requests during a support session:

```sql DQL theme={null}
ALTER SYSTEM SET support_bundle_request_rate = 10
```

> [!NOTE]
>
> There is no total byte-size cap on retained bundles. Retention is count-based.
> Bundle size is driven mainly by retained log volume and the dial9 buffer size
> (`dial9_buffer_size_mb`, default 16 MB).

## Requesting a bundle from the Portal

You can also request a support bundle from a managed device through the Ditto
Portal. See [Troubleshooting](https://docs.ditto.live/cloud/portal/troubleshooting.md). The Portal-driven archive uses
the same base entries and adds `request_history.json` and
`system_indexes.json`, plus a flamegraph when
`remote_flamegraph_request_enabled` is on. Portal-triggered bundles upload to
Ditto Server automatically. Locally requested bundles stay on the device until
you fetch and share them.


## Related topics

- [SDK Logging](https://docs.ditto.live/sdk/latest/deployment/logging.md)
- [Virtual Collections](https://docs.ditto.live/dql/virtual-collections.md)
- [Optimizing Bundle Sizes for Android](https://docs.ditto.live/best-practices/optimizing-bundle-sizes-for-android.md)
