diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 17e3f78..e3b29c3 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -1,6 +1,6 @@
---
title: "Audit Logs"
-description: "Search and export audit logs for API requests across your organization"
+description: "Audit logs for API requests across your organization"
---
Audit logs record authenticated API requests across your entire organization. Use them to review who called Kernel, which endpoint they called, when the request happened, and how the request completed.
@@ -10,15 +10,16 @@ Choose the endpoint that matches the amount of data you need:
| Endpoint | Best for | Output |
|----------|----------|--------|
| [Search](#search-audit-logs) | Interactive investigation and recent activity | Paginated JSON events |
-| [Export](#export-audit-logs) | Archival, compliance, and offline analysis | Gzip-compressed JSON Lines (`.jsonl.gz`) |
+| [Download](#download-audit-logs) | Archival, compliance, and offline analysis of a bounded time window | Gzip-compressed JSON Lines (`.jsonl.gz`) |
+| [Continuous S3 export](#continuous-s3-export) | Ongoing delivery to your data lake or compliance bucket | Partitioned `jsonl.gz` objects in your S3 bucket |
-Audit logs are ordered newest first. Time windows use an inclusive `start` and exclusive `end`: `[start, end)`. A search or export can cover up to 30 days. Split longer periods into multiple time windows.
+Search and download are available on **Start-Up** and **Enterprise** plans. Continuous S3 export is available on **Enterprise**.
-Both endpoints are also available from the [CLI](/reference/cli/audit-logs). For the underlying HTTP API, see [search](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) and [export](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) in the API reference.
+Audit logs are also available from the [CLI](/reference/cli/audit-logs).
## Filter audit logs
-The API and SDKs use the same filters for search and export:
+The API and SDKs use the same filters for search and downloads:
- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
- `service` filters by the service that emitted the audit event.
@@ -95,14 +96,9 @@ func main() {
See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the full request and response schema.
-## Export audit logs
+## Download audit logs
-The SDK download helpers default to `jsonl.gz` and write a complete export to a destination you provide. They:
-
-- request every chunk until the export is complete
-- validate pagination metadata and each chunk's SHA-256 checksum before writing
-- retry transient HTTP and transfer failures
-- append verified chunks in order
+A download covers a time window of up to 30 days. The SDK download helpers default to `jsonl.gz` and write the result to a destination you provide.
The helpers don't close the destination. Python provides equivalent sync and async methods; both accept a synchronous binary destination.
@@ -178,6 +174,257 @@ func main() {
```
-Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`.
+Downloaded chunks contain one JSON object per line. They use the same fields as search results and add `event_id`.
For direct HTTP integrations, see the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for pagination headers, formats, and the full request and response schema.
+
+## Continuous S3 export
+
+Continuous export writes new audit log events to an S3 bucket that you control. A destination is an organization-level resource: Kernel assumes an IAM role in your AWS account and writes `jsonl.gz` objects to the configured bucket. The destination is created paused so you can configure and verify IAM before delivery starts.
+
+### Set up a destination
+
+You can use the SDKs, the [CLI](/reference/cli/audit-logs#kernel-audit-logs-export), or the HTTP API. All destination requests require an organization-level credential.
+
+The setup steps below use the SDKs. For the CLI walkthrough, see the [CLI reference](/reference/cli/audit-logs#kernel-audit-logs-export).
+
+#### 1. Create a paused destination
+
+Create the destination with the customer role ARN. The create response contains the destination `id`, the Kernel role ARN that must be trusted (`kernel_role_arn`), and the unique STS external ID (`external_id`). Save all three values. The `external_id` is not your organization ID and is not interchangeable with an external ID from another destination.
+
+Every destination is an S3 destination in `jsonl.gz` format and starts with `status: "paused"`. If you use KMS, set `kms_key_id` to a key ID, alias, or ARN in the destination region.
+
+
+```typescript TypeScript
+import Kernel from '@onkernel/sdk';
+
+const kernel = new Kernel({
+ apiKey: process.env.KERNEL_API_KEY,
+});
+
+const destination = await kernel.auditLogs.exportDestinations.create({
+ type: 's3',
+ format: 'jsonl.gz',
+ region: 'us-east-1',
+ bucket: 'customer-audit-logs',
+ prefix: 'audit-logs',
+ role_arn: 'arn:aws:iam::123456789012:role/customer-audit-log-export',
+ // kms_key_id: 'arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555',
+});
+
+console.log(destination.id);
+console.log(destination.kernel_role_arn);
+console.log(destination.external_id);
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+destination = client.audit_logs.export_destinations.create(
+ type="s3",
+ format="jsonl.gz",
+ region="us-east-1",
+ bucket="customer-audit-logs",
+ prefix="audit-logs",
+ role_arn="arn:aws:iam::123456789012:role/customer-audit-log-export",
+ # kms_key_id="arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555",
+)
+
+print(destination.id)
+print(destination.kernel_role_arn)
+print(destination.external_id)
+```
+
+```go Go
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/kernel/kernel-go-sdk"
+)
+
+func main() {
+ ctx := context.Background()
+ client := kernel.NewClient()
+
+ destination, err := client.AuditLogs.ExportDestinations.New(ctx, kernel.AuditLogExportDestinationNewParams{
+ CreateAuditLogExportDestinationRequest: kernel.CreateAuditLogExportDestinationRequestParam{
+ Type: kernel.CreateAuditLogExportDestinationRequestTypeS3,
+ Format: kernel.CreateAuditLogExportDestinationRequestFormatJSONLGz,
+ Region: "us-east-1",
+ Bucket: "customer-audit-logs",
+ Prefix: "audit-logs",
+ RoleArn: "arn:aws:iam::123456789012:role/customer-audit-log-export",
+ // KmsKeyID: kernel.String("arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555"),
+ },
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Println(destination.ID)
+ fmt.Println(destination.KernelRoleArn)
+ fmt.Println(destination.ExternalID)
+}
+```
+
+
+#### 2. Configure the IAM trust policy
+
+Update the trust policy on the customer role supplied as `role_arn`. Use the `kernel_role_arn` and `external_id` returned by the create call. The values below are placeholders; replace both of them with the values from your response.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "AllowKernelAuditLogExport",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:aws:iam::123456789012:role/kernel-audit-log-export"
+ },
+ "Action": "sts:AssumeRole",
+ "Condition": {
+ "StringEquals": {
+ "sts:ExternalId": "external-id-from-create-response"
+ }
+ }
+ }
+ ]
+}
+```
+
+`Principal.AWS` must be the returned `kernel_role_arn`, not the customer role's own ARN. The `sts:ExternalId` condition must be the returned `external_id`. Recreating a destination generates a new external ID, so update this trust policy again if you recreate it.
+
+#### 3. Grant S3 and KMS permissions
+
+Attach an identity policy to the customer role. Grant `s3:PutObject` for real delivery and the test probe. Granting `s3:DeleteObject` is recommended so Kernel can remove the temporary probe after a successful test.
+
+The resource should match the configured prefix. If you use a different prefix, replace `audit-logs/*` below. Omit the KMS statement when `kms_key_id` is empty.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "WriteAuditLogObjects",
+ "Effect": "Allow",
+ "Action": [
+ "s3:PutObject",
+ "s3:DeleteObject"
+ ],
+ "Resource": "arn:aws:s3:::customer-audit-logs/audit-logs/*"
+ },
+ {
+ "Sid": "UseAuditLogKMSKey",
+ "Effect": "Allow",
+ "Action": "kms:GenerateDataKey",
+ "Resource": "arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555"
+ }
+ ]
+}
+```
+
+When KMS is configured, the KMS key policy must also allow the customer role to use `kms:GenerateDataKey` (unless the key policy delegates access to IAM policies). The test and real uploads use the same encryption request: SSE-KMS with the configured key. Without a configured KMS key, the bucket's default encryption applies.
+
+#### 4. Test and activate
+
+The test endpoint assumes the customer role, writes a temporary gzip probe, and attempts to delete it. The probe uses the same request metadata as a real delivery: a SHA-256 checksum, `Content-Type: application/gzip`, and SSE-KMS when configured. Run the test before changing the destination to active.
+
+A successful test returns `stage: "complete"`. A failed test identifies `assume_role` or `put_object` and returns `assume_role_failed` or `put_object_failed`. Do not activate the destination until the test succeeds.
+
+
+```typescript TypeScript
+const test = await kernel.auditLogs.exportDestinations.test(destination.id);
+if (!test.success) {
+ throw new Error(`export test failed at ${test.stage}`);
+}
+
+const active = await kernel.auditLogs.exportDestinations.update(destination.id, {
+ status: 'active',
+});
+console.log(active.status); // active
+```
+
+```python Python
+test = client.audit_logs.export_destinations.test(destination.id)
+if not test.success:
+ raise RuntimeError(f"export test failed at {test.stage}")
+
+active = client.audit_logs.export_destinations.update(
+ destination.id,
+ status="active",
+)
+print(active.status) # active
+```
+
+```go Go
+test, err := client.AuditLogs.ExportDestinations.Test(ctx, destination.ID)
+if err != nil {
+ panic(err)
+}
+if !test.Success {
+ panic(fmt.Sprintf("export test failed at %s", test.Stage))
+}
+
+active, err := client.AuditLogs.ExportDestinations.Update(ctx, destination.ID, kernel.AuditLogExportDestinationUpdateParams{
+ UpdateAuditLogExportDestinationRequest: kernel.UpdateAuditLogExportDestinationRequestParam{
+ Status: kernel.UpdateAuditLogExportDestinationRequestStatusActive,
+ },
+})
+if err != nil {
+ panic(err)
+}
+fmt.Println(active.Status) // active
+```
+
+
+Activation starts delivery at the activation time. It does not backfill events recorded before activation.
+
+### Object layout
+
+Every continuous export object uses this exact key layout:
+
+```
+/destination_id=/org_id=/date=/hour=/-.jsonl.gz
+```
+
+For example, a destination with prefix `audit-logs` writes under `audit-logs/destination_id=...`. If the prefix is empty, the key starts with `destination_id=...` and has no leading slash. The `date` and `hour` partitions are UTC and identify the calendar hour that fully contains every row in the object. This makes the layout safe for Hive-style partitioning. The format is always `jsonl.gz`: each decompressed line is one JSON event, including `event_id`.
+
+### Delivery semantics
+
+- Delivery is **at least once**. A retry can rewrite the same object.
+- Each event-time window is held for about 10 minutes before it commits. A row that becomes visible after its window has committed may not be delivered.
+- Delivery begins at activation time; events before activation are not backfilled.
+- Pausing stops new delivery attempts. Resuming starts from the resume time; events recorded while paused are never exported. Pausing is not a way to defer delivery.
+- An S3 upload already in progress may complete after a pause or delete. Its rows can appear again after the destination is resumed.
+
+### Monitor delivery
+
+Use the SDK retrieve and list methods, the [CLI](/reference/cli/audit-logs#kernel-audit-logs-export), or the HTTP GET endpoints to inspect the destination and its delivery health. The health fields are:
+
+| Field | Meaning |
+|-------|---------|
+| `status` | `active` or `paused`. |
+| `last_exported_cursor` | Opaque forward-only checkpoint for continuous delivery. It is not an audit-log list page token. |
+| `last_success_at` | Time of the most recent successful export upload. |
+| `last_error` | Sanitized description of the most recent delivery failure. |
+| `last_error_at` | Time of the most recent delivery failure. |
+| `consecutive_failures` | Number of consecutive failed delivery attempts. |
+| `next_attempt_at` | Scheduled time for the next delivery attempt after a failure. |
+
+The cursor and timestamp/error fields can be absent until the destination has attempted delivery. Use `last_error`, `last_error_at`, and `consecutive_failures` together when investigating a destination that is active but not progressing.
+
+### Troubleshoot delivery
+
+- **`assume_role` / `assume_role_failed`:** Check the customer role's trust policy. The principal must be the current `kernel_role_arn` from the destination response, and `sts:ExternalId` must equal the current `external_id`. Recreating a destination issues a new external ID; update the trust policy after recreation.
+- **`put_object` / `put_object_failed`:** Confirm that the customer role can write to the configured bucket and prefix with `s3:PutObject`, and that the bucket is in the configured commercial AWS region. Grant `s3:DeleteObject` as well so the test probe can be cleaned up.
+- **KMS failures:** Confirm that the KMS key is in the commercial `aws` partition, that an ARN's region matches the destination region, and that both the role policy and key policy allow `kms:GenerateDataKey`. If you do not need a customer-managed key, clear it and use bucket-default encryption.
+- **A probe remains after a successful test:** Probe cleanup is best effort. Add `s3:DeleteObject` for the configured prefix and remove any leftover `.kernel-audit-log-export-test-*.jsonl.gz` object yourself.
+- **A `PATCH` returns `409 Conflict`:** The destination changed concurrently. Retrieve fresh state, merge your intended fields with that state, and retry the update. Do not retry a stale read-modify-write payload unchanged.
+- **No objects after a pause or downgrade:** Check `status` and the plan. Pausing and non-Enterprise status stop new delivery; restore Enterprise and follow the normal activation/resume path if the destination is paused. Events recorded while paused are not backfilled.
diff --git a/reference/cli/audit-logs.mdx b/reference/cli/audit-logs.mdx
index 300272c..91f565f 100644
--- a/reference/cli/audit-logs.mdx
+++ b/reference/cli/audit-logs.mdx
@@ -2,10 +2,16 @@
title: "Audit Logs"
---
-Search and download [organization audit logs](/info/audit-logs) from the CLI.
+Search, download, and manage [organization audit logs](/info/audit-logs) from the CLI.
+
+Search and download are available on **Start-Up** and **Enterprise** plans. Continuous S3 export is available on **Enterprise**.
Time values can be dates (`2026-06-01`) or timestamps (`2026-06-01T15:04:05Z`). Dates begin at midnight UTC.
+
+ The `kernel audit-logs export` group follows the current CLI implementation. Install a CLI release that includes this command group; availability depends on the release you have installed.
+
+
## `kernel audit-logs search`
Search audit logs within a time window. Results are ordered newest first.
@@ -88,6 +94,166 @@ The requested output appears only after the full download succeeds. Failed downl
Downloads don't resume: rerunning the command starts over. Existing files are replaced only when you pass `--force`.
+## `kernel audit-logs export`
+
+Manage S3 destinations that receive a continuous export of your organization's audit logs. The group is also available as `kernel audit-logs exports` and `kernel audit-logs export-destinations`.
+
+Objects use the layout `/destination_id=/org_id=/date=/hour=/-.jsonl.gz`. Delivery is at-least-once.
+
+Create a destination paused, configure the IAM trust and permissions, run `test`, and then run `resume`. The create response includes the `kernel_role_arn` and `external_id` values needed for the trust policy. The destination must use an organization-level credential; project-scoped API keys are refused.
+
+### `kernel audit-logs export create`
+
+Create an S3 audit log export destination. The destination is created paused.
+
+```bash
+kernel audit-logs export create \
+ --region us-east-1 \
+ --bucket customer-audit-logs \
+ --prefix audit-logs \
+ --role-arn arn:aws:iam::123456789012:role/customer-audit-log-export \
+ --output json
+```
+
+Use `--kms-key-id` when the destination should use SSE-KMS. The command always creates the destination with the `s3` type and `jsonl.gz` format.
+
+| Flag | Description |
+|------|-------------|
+| `--region ` | AWS region of the destination bucket. Required. |
+| `--bucket ` | Destination S3 bucket name. Required. |
+| `--prefix ` | Key prefix for exported objects; may be empty. Required. |
+| `--role-arn ` | IAM role ARN Kernel assumes to deliver logs. Required. |
+| `--kms-key-id ` | KMS key ID, alias, or ARN for server-side encryption. |
+| `--output json`, `-o json` | Output the raw JSON destination object. |
+
+The human-readable output prints the destination details and the activation steps. Use JSON output when a script needs to capture `id`, `kernel_role_arn`, and `external_id`.
+
+### `kernel audit-logs export list`
+
+List audit log export destinations. Human-readable output includes the destination ID, bucket, prefix, region, status, last success, failure count, and last error.
+
+```bash
+kernel audit-logs export list \
+ --limit 20 \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--limit ` | Maximum number of destinations to return, from `1` to `100`. Defaults to `20`. |
+| `--output json`, `-o json` | Output a JSON object with `destinations` and `next_offset` when another page exists. |
+
+### `kernel audit-logs export get `
+
+Get details for one audit log export destination.
+
+```bash
+kernel audit-logs export get y2kkbpcz1lg0h3q6yr8x4m7d \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--output json`, `-o json` | Output the raw JSON destination object. |
+
+Without `--output json`, the command prints configuration, status, and delivery health fields such as `last_exported_cursor`, `last_success_at`, `last_error`, `last_error_at`, `consecutive_failures`, and `next_attempt_at`.
+
+### `kernel audit-logs export update `
+
+Update one or more destination fields. Pass at least one update flag. The API validates the merged destination configuration, so a KMS key ARN must match the destination region.
+
+```bash
+kernel audit-logs export update y2kkbpcz1lg0h3q6yr8x4m7d \
+ --prefix audit-logs-v2 \
+ --output json
+```
+
+To remove a configured KMS key and use bucket-default encryption:
+
+```bash
+kernel audit-logs export update y2kkbpcz1lg0h3q6yr8x4m7d \
+ --clear-kms-key
+```
+
+| Flag | Description |
+|------|-------------|
+| `--region ` | Update the AWS region of the destination bucket. |
+| `--bucket ` | Update the destination S3 bucket name. |
+| `--prefix ` | Update the key prefix for exported objects. |
+| `--role-arn ` | Update the IAM role ARN Kernel assumes to deliver logs. |
+| `--kms-key-id ` | Update the KMS key ID, alias, or ARN for server-side encryption. |
+| `--clear-kms-key` | Remove the configured KMS key. Mutually exclusive with `--kms-key-id`. |
+| `--output json`, `-o json` | Output the raw JSON destination object. |
+
+A successful update prints the updated destination. A `409 Conflict` means the destination changed concurrently; retrieve fresh state and retry with the intended fields.
+
+### `kernel audit-logs export pause `
+
+Pause a destination so new delivery attempts stop.
+
+```bash
+kernel audit-logs export pause y2kkbpcz1lg0h3q6yr8x4m7d \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--output json`, `-o json` | Output the raw JSON destination object. |
+
+An S3 upload already in progress may still complete after the pause. Events recorded while paused are not exported.
+
+### `kernel audit-logs export resume `
+
+Resume a destination. Delivery starts from the time of the resume; events recorded while paused are not exported.
+
+```bash
+kernel audit-logs export resume y2kkbpcz1lg0h3q6yr8x4m7d \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--output json`, `-o json` | Output the raw JSON destination object. |
+
+Use `resume` only after the destination's trust policy and permissions are configured and `test` succeeds.
+
+### `kernel audit-logs export delete `
+
+Delete a destination and stop new delivery attempts.
+
+```bash
+kernel audit-logs export delete y2kkbpcz1lg0h3q6yr8x4m7d
+```
+
+This command has no flags and does not support JSON output. An S3 upload already in progress may complete after the delete.
+
+### `kernel audit-logs export test `
+
+Test a destination by assuming its role and writing a temporary probe object. The command exits non-zero when the test fails.
+
+```bash
+kernel audit-logs export test y2kkbpcz1lg0h3q6yr8x4m7d \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--output json`, `-o json` | Output the raw JSON test result. |
+
+The result has `success` and `stage` fields. The stages are `assume_role`, `put_object`, and `complete`; customer-fixable failures use `assume_role_failed` or `put_object_failed`. A successful test reaches `complete`. The probe is deleted after the write when the role has `s3:DeleteObject`; grant that permission so test objects are cleaned up.
+
+### Activation sequence
+
+Use this sequence for every new destination:
+
+1. Run `create`. Save the returned destination ID, `kernel_role_arn`, and `external_id`.
+2. Update the customer role's trust policy to allow the returned Kernel role ARN with the returned `sts:ExternalId`.
+3. Grant `s3:PutObject` and, preferably, `s3:DeleteObject` on the configured prefix. If KMS is configured, grant `kms:GenerateDataKey` and key-policy access.
+4. Run `test ` and fix any `assume_role` or `put_object` failure.
+5. Run `resume ` to start delivery from that point. There is no backfill.
+
+After a plan downgrade, `list`, `get`, and `delete` remain available for cleanup, while delivery stops and `create`, `update`, and `test` require Enterprise.
+
## Aliases
You can also use `kernel audit-log`, `kernel auditlogs`, or `kernel auditlog`.