From 92af4fa44ef157d26c72711617141e7f223705ab Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:44:25 +0000
Subject: [PATCH 01/19] Document audit logs API
---
docs.json | 1 +
info/audit-logs.mdx | 130 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 131 insertions(+)
create mode 100644 info/audit-logs.mdx
diff --git a/docs.json b/docs.json
index 9b0eef6..f3d9ee9 100644
--- a/docs.json
+++ b/docs.json
@@ -116,6 +116,7 @@
]
},
"info/api-keys",
+ "info/audit-logs",
"browsers/file-io",
"browsers/curl",
"browsers/ssh",
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
new file mode 100644
index 0000000..b4a6e41
--- /dev/null
+++ b/info/audit-logs.mdx
@@ -0,0 +1,130 @@
+---
+title: "Audit Logs"
+description: "List organization API request audit events"
+---
+
+Audit logs record authenticated API requests for your organization. Use them to review who called Kernel, which endpoint they called, when the request happened, and how the request completed.
+
+Audit logs are ordered newest first. The `start` timestamp is inclusive, the `end` timestamp is exclusive, and each request can cover up to 30 days. Each page returns up to 100 events.
+
+## List audit logs
+
+Use the SDKs to page through logs for a time window.
+
+
+```typescript TypeScript
+import Kernel from '@onkernel/sdk';
+
+const kernel = new Kernel({
+ apiKey: process.env.KERNEL_API_KEY,
+});
+
+for await (const event of kernel.auditLogs.list({
+ start: '2026-06-01T00:00:00Z',
+ end: '2026-06-02T00:00:00Z',
+ limit: 100,
+ method: 'POST',
+})) {
+ console.log(event.timestamp, event.method, event.path, event.status);
+}
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+for event in client.audit_logs.list(
+ start="2026-06-01T00:00:00Z",
+ end="2026-06-02T00:00:00Z",
+ limit=100,
+ method="POST",
+):
+ print(event.timestamp, event.method, event.path, event.status)
+```
+
+
+## Filter audit logs
+
+Use filters to narrow the time window to the events you need:
+
+- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
+- `service` filters by the service that emitted the audit event.
+- `method` filters by HTTP method.
+- `exclude_method` excludes a single HTTP method.
+- `search` matches path, user ID, email, client IP, and status.
+- `search_user_id` adds one or more user IDs to the search.
+
+
+```typescript TypeScript
+const logs = kernel.auditLogs.list({
+ start: '2026-06-01T00:00:00Z',
+ end: '2026-06-08T00:00:00Z',
+ limit: 50,
+ auth_strategy: 'api_key',
+ exclude_method: 'GET',
+ search: '/browsers',
+});
+
+for await (const event of logs) {
+ console.log(event.email, event.client_ip, event.user_agent);
+}
+```
+
+```python Python
+logs = client.audit_logs.list(
+ start="2026-06-01T00:00:00Z",
+ end="2026-06-08T00:00:00Z",
+ limit=50,
+ auth_strategy="api_key",
+ exclude_method="GET",
+ search="/browsers",
+)
+
+for event in logs:
+ print(event.email, event.client_ip, event.user_agent)
+```
+
+
+## Page with HTTP
+
+The API returns the next cursor in the `X-Next-Page-Token` response header. Pass it back as `page_token` to read the next page.
+
+```bash
+curl --get https://api.onkernel.com/audit-logs \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "limit=100"
+```
+
+```bash
+curl --get https://api.onkernel.com/audit-logs \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "limit=100" \
+ --data-urlencode "page_token=eyJ0IjoiMjAyNi0wNi0wMVQyMzo1OTo1OS43NDUxMjNaIn0"
+```
+
+Use `X-Has-More: true` to decide whether to continue paging. Page tokens are opaque; store and pass them exactly as returned.
+
+## Response fields
+
+Each audit log entry includes:
+
+- `timestamp`
+- `auth_strategy`
+- `user_id`
+- `email`
+- `status`
+- `method`
+- `path`
+- `route`
+- `domain`
+- `duration_ms`
+- `client_ip`
+- `user_agent`
+
+See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the full request and response schema.
From 6392fd86ff26ec59dc53d297c53d4b557b08de73 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Tue, 23 Jun 2026 14:47:35 +0000
Subject: [PATCH 02/19] Add Go audit log examples
---
info/audit-logs.mdx | 64 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index b4a6e41..788b53e 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -43,6 +43,37 @@ for event in client.audit_logs.list(
):
print(event.timestamp, event.method, event.path, event.status)
```
+
+```go Go
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/kernel/kernel-go-sdk"
+)
+
+func main() {
+ ctx := context.Background()
+ client := kernel.NewClient()
+
+ pager := client.AuditLogs.ListAutoPaging(ctx, kernel.AuditLogListParams{
+ Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
+ End: time.Date(2026, time.June, 2, 0, 0, 0, 0, time.UTC),
+ Limit: kernel.Int(100),
+ Method: kernel.String("POST"),
+ })
+ for pager.Next() {
+ event := pager.Current()
+ fmt.Println(event.Timestamp, event.Method, event.Path, event.Status)
+ }
+ if err := pager.Err(); err != nil {
+ panic(err)
+ }
+}
+```
## Filter audit logs
@@ -85,6 +116,39 @@ logs = client.audit_logs.list(
for event in logs:
print(event.email, event.client_ip, event.user_agent)
```
+
+```go Go
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/kernel/kernel-go-sdk"
+)
+
+func main() {
+ ctx := context.Background()
+ client := kernel.NewClient()
+
+ pager := client.AuditLogs.ListAutoPaging(ctx, kernel.AuditLogListParams{
+ Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
+ End: time.Date(2026, time.June, 8, 0, 0, 0, 0, time.UTC),
+ Limit: kernel.Int(50),
+ AuthStrategy: kernel.String("api_key"),
+ ExcludeMethod: kernel.String("GET"),
+ Search: kernel.String("/browsers"),
+ })
+ for pager.Next() {
+ event := pager.Current()
+ fmt.Println(event.Email, event.ClientIP, event.UserAgent)
+ }
+ if err := pager.Err(); err != nil {
+ panic(err)
+ }
+}
+```
## Page with HTTP
From 3b91857722993fe6f10ad32ac13303def764eba4 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Wed, 24 Jun 2026 19:14:07 +0000
Subject: [PATCH 03/19] Clarify audit log filter wording and show paging
headers
Filters narrow results, not the time window; add --include to the HTTP
paging examples so the X-Next-Page-Token response header is visible.
---
info/audit-logs.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 788b53e..b8cde9d 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -78,7 +78,7 @@ func main() {
## Filter audit logs
-Use filters to narrow the time window to the events you need:
+Use filters to narrow results to the events you need:
- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
- `service` filters by the service that emitted the audit event.
@@ -156,7 +156,7 @@ func main() {
The API returns the next cursor in the `X-Next-Page-Token` response header. Pass it back as `page_token` to read the next page.
```bash
-curl --get https://api.onkernel.com/audit-logs \
+curl --include --get https://api.onkernel.com/audit-logs \
--header "Authorization: Bearer $KERNEL_API_KEY" \
--data-urlencode "start=2026-06-01T00:00:00Z" \
--data-urlencode "end=2026-06-02T00:00:00Z" \
@@ -164,7 +164,7 @@ curl --get https://api.onkernel.com/audit-logs \
```
```bash
-curl --get https://api.onkernel.com/audit-logs \
+curl --include --get https://api.onkernel.com/audit-logs \
--header "Authorization: Bearer $KERNEL_API_KEY" \
--data-urlencode "start=2026-06-01T00:00:00Z" \
--data-urlencode "end=2026-06-02T00:00:00Z" \
From e4dbf8bb1bd394c0f4f02c2fd1560ebb7a7d6a9b Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Wed, 24 Jun 2026 21:11:16 +0000
Subject: [PATCH 04/19] Clarify audit log date range limit
---
info/audit-logs.mdx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index b8cde9d..f93dbe4 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -7,6 +7,8 @@ Audit logs record authenticated API requests for your organization. Use them to
Audit logs are ordered newest first. The `start` timestamp is inclusive, the `end` timestamp is exclusive, and each request can cover up to 30 days. Each page returns up to 100 events.
+Keep each audit log request to a 30-day range or less. For longer investigations, split the query into multiple time windows.
+
## List audit logs
Use the SDKs to page through logs for a time window.
From 1ac1f19aaa62e5e71969573625b39bf7aa345ac3 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Thu, 16 Jul 2026 19:13:22 +0000
Subject: [PATCH 05/19] Document audit log search and export workflows
---
docs.json | 1 +
info/audit-logs.mdx | 241 ++++++++++++++++++++++++++++++-----
reference/cli.mdx | 3 +
reference/cli/audit-logs.mdx | 93 ++++++++++++++
4 files changed, 303 insertions(+), 35 deletions(-)
create mode 100644 reference/cli/audit-logs.mdx
diff --git a/docs.json b/docs.json
index f3d9ee9..4e93dd5 100644
--- a/docs.json
+++ b/docs.json
@@ -290,6 +290,7 @@
"reference/cli/managed-auth",
"reference/cli/projects",
"reference/cli/api-keys",
+ "reference/cli/audit-logs",
"reference/cli/mcp"
]
}
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index f93dbe4..01aaaf1 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -1,17 +1,49 @@
---
title: "Audit Logs"
-description: "List organization API request audit events"
+description: "Search and export organization API request audit events"
---
Audit logs record authenticated API requests for your organization. Use them to review who called Kernel, which endpoint they called, when the request happened, and how the request completed.
-Audit logs are ordered newest first. The `start` timestamp is inclusive, the `end` timestamp is exclusive, and each request can cover up to 30 days. Each page returns up to 100 events.
+Choose the workflow that matches the amount of data you need:
-Keep each audit log request to a 30-day range or less. For longer investigations, split the query into multiple time windows.
+| Workflow | Best for | Output |
+|---|---|---|
+| Search | Interactive investigation and recent activity | A table, JSON array, or paginated SDK results |
+| Export | Archival, compliance, and offline analysis | JSON Lines (`.jsonl`) or gzip-compressed JSON Lines (`.jsonl.gz`) |
-## List audit logs
+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.
-Use the SDKs to page through logs for a time window.
+## Search with the CLI
+
+Search the previous 24 hours:
+
+```bash
+kernel audit-logs search
+```
+
+Add a time window and filters:
+
+```bash
+kernel audit-logs search \
+ --start 2026-06-01 \
+ --end 2026-06-08 \
+ --search /browsers \
+ --limit 500 \
+ --output json
+```
+
+The CLI accepts RFC 3339 timestamps or `YYYY-MM-DD` dates. Date-only values represent midnight UTC.
+
+
+ The CLI excludes `GET` requests by default to reduce noise. Pass `--include-get` to include them, or `--method GET` to return only `GET` requests. The API and SDKs don't exclude `GET` unless you set `exclude_method`.
+
+
+See the [audit log CLI reference](/reference/cli/audit-logs) for all flags and defaults.
+
+## Search with an SDK
+
+Each API page contains up to 100 events. The SDK pagination helpers request older pages as you iterate.
```typescript TypeScript
@@ -80,14 +112,14 @@ func main() {
## Filter audit logs
-Use filters to narrow results to the events you need:
+The API and SDKs use the same filters for search and export:
- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
- `service` filters by the service that emitted the audit event.
-- `method` filters by HTTP method.
-- `exclude_method` excludes a single HTTP method.
+- `method` includes one HTTP method.
+- `exclude_method` excludes one or more HTTP methods, with up to 10 values.
- `search` matches path, user ID, email, client IP, and status.
-- `search_user_id` adds one or more user IDs to the search.
+- `search_user_id` adds up to 100 user IDs to the free-text search.
```typescript TypeScript
@@ -96,7 +128,7 @@ const logs = kernel.auditLogs.list({
end: '2026-06-08T00:00:00Z',
limit: 50,
auth_strategy: 'api_key',
- exclude_method: 'GET',
+ exclude_method: ['GET'],
search: '/browsers',
});
@@ -111,7 +143,7 @@ logs = client.audit_logs.list(
end="2026-06-08T00:00:00Z",
limit=50,
auth_strategy="api_key",
- exclude_method="GET",
+ exclude_method=["GET"],
search="/browsers",
)
@@ -135,12 +167,12 @@ func main() {
client := kernel.NewClient()
pager := client.AuditLogs.ListAutoPaging(ctx, kernel.AuditLogListParams{
- Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
- End: time.Date(2026, time.June, 8, 0, 0, 0, 0, time.UTC),
- Limit: kernel.Int(50),
- AuthStrategy: kernel.String("api_key"),
- ExcludeMethod: kernel.String("GET"),
- Search: kernel.String("/browsers"),
+ Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
+ End: time.Date(2026, time.June, 8, 0, 0, 0, 0, time.UTC),
+ Limit: kernel.Int(50),
+ AuthStrategy: kernel.String("api_key"),
+ ExcludeMethod: []string{"GET"},
+ Search: kernel.String("/browsers"),
})
for pager.Next() {
event := pager.Current()
@@ -155,7 +187,13 @@ func main() {
## Page with HTTP
-The API returns the next cursor in the `X-Next-Page-Token` response header. Pass it back as `page_token` to read the next page.
+The REST API returns pagination state in these response headers:
+
+- `X-Limit` is the page size applied to the request.
+- `X-Has-More` indicates whether older records remain.
+- `X-Next-Page-Token` contains the opaque token for the next page.
+
+Request the first page:
```bash
curl --include --get https://api.onkernel.com/audit-logs \
@@ -165,32 +203,165 @@ curl --include --get https://api.onkernel.com/audit-logs \
--data-urlencode "limit=100"
```
+If `X-Has-More` is `true`, copy the `X-Next-Page-Token` value and pass it unchanged:
+
```bash
curl --include --get https://api.onkernel.com/audit-logs \
--header "Authorization: Bearer $KERNEL_API_KEY" \
--data-urlencode "start=2026-06-01T00:00:00Z" \
--data-urlencode "end=2026-06-02T00:00:00Z" \
--data-urlencode "limit=100" \
- --data-urlencode "page_token=eyJ0IjoiMjAyNi0wNi0wMVQyMzo1OTo1OS43NDUxMjNaIn0"
+ --data-urlencode "page_token=$NEXT_PAGE_TOKEN"
+```
+
+Page tokens are opaque. Don't decode, construct, or modify them.
+
+## Download an export with the CLI
+
+Use the CLI to download every chunk in a time window into one verified gzip-compressed JSONL file:
+
+```bash
+kernel audit-logs download \
+ --start 2026-06-01 \
+ --end 2026-07-01 \
+ --to audit-june.jsonl.gz
+```
+
+The CLI:
+
+1. downloads up to 50,000 records per chunk,
+2. verifies each chunk against its `X-Content-Sha256` checksum,
+3. retries transient and integrity failures,
+4. writes to a temporary `.partial` file, and
+5. publishes the final file only after every chunk succeeds.
+
+Incomplete downloads are removed and don't resume across CLI runs. Rerunning the command starts the export again. Use `--force` to replace an existing output file.
+
+Like CLI search, CLI download excludes `GET` by default. Search and download accept the same filters.
+
+## Download an export chunk with HTTP
+
+The export API returns one chunk per request. Save the response headers separately from the binary body:
+
+```bash
+curl --fail-with-body --silent --show-error --get \
+ https://api.onkernel.com/audit-logs/export/chunk \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "format=jsonl.gz" \
+ --dump-header audit-logs-chunk.headers \
+ --output audit-logs-chunk.jsonl.gz
+```
+
+When `X-Has-More` is `true`, copy `X-Next-Cursor` and pass it unchanged to download the next chunk:
+
+```bash
+curl --fail-with-body --silent --show-error --get \
+ https://api.onkernel.com/audit-logs/export/chunk \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "format=jsonl.gz" \
+ --data-urlencode "cursor=$NEXT_CURSOR" \
+ --dump-header audit-logs-next-chunk.headers \
+ --output audit-logs-next-chunk.jsonl.gz
+```
+
+## Download an export chunk with an SDK
+
+Save the response body exactly as returned, then inspect `X-Has-More`. When it is `true`, pass `X-Next-Cursor` as `cursor` on the next request.
+
+
+```typescript TypeScript
+import { writeFile } from 'node:fs/promises';
+import Kernel from '@onkernel/sdk';
+
+const kernel = new Kernel({
+ apiKey: process.env.KERNEL_API_KEY,
+});
+
+const response = await kernel.auditLogs.exportChunk({
+ start: '2026-06-01T00:00:00Z',
+ end: '2026-06-02T00:00:00Z',
+ format: 'jsonl.gz',
+ exclude_method: ['GET'],
+});
+
+await writeFile(
+ 'audit-logs-chunk.jsonl.gz',
+ Buffer.from(await response.arrayBuffer()),
+);
+
+console.log(response.headers.get('x-has-more'));
+console.log(response.headers.get('x-next-cursor'));
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+response = client.audit_logs.export_chunk(
+ start="2026-06-01T00:00:00Z",
+ end="2026-06-02T00:00:00Z",
+ format="jsonl.gz",
+ exclude_method=["GET"],
+)
+response.write_to_file("audit-logs-chunk.jsonl.gz")
+
+print(response.http_response.headers.get("x-has-more"))
+print(response.http_response.headers.get("x-next-cursor"))
```
-Use `X-Has-More: true` to decide whether to continue paging. Page tokens are opaque; store and pass them exactly as returned.
+```go Go
+package main
+
+import (
+ "context"
+ "io"
+ "os"
+ "time"
+
+ "github.com/kernel/kernel-go-sdk"
+)
+
+func main() {
+ ctx := context.Background()
+ client := kernel.NewClient()
+
+ response, err := client.AuditLogs.ExportChunk(ctx, kernel.AuditLogExportChunkParams{
+ Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
+ End: time.Date(2026, time.June, 2, 0, 0, 0, 0, time.UTC),
+ Format: kernel.AuditLogExportChunkParamsFormatJSONLGz,
+ ExcludeMethod: []string{"GET"},
+ })
+ if err != nil {
+ panic(err)
+ }
+ defer response.Body.Close()
+
+ file, err := os.Create("audit-logs-chunk.jsonl.gz")
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
-## Response fields
+ if _, err := io.Copy(file, response.Body); err != nil {
+ panic(err)
+ }
+
+ println(response.Header.Get("X-Has-More"))
+ println(response.Header.Get("X-Next-Cursor"))
+}
+```
+
-Each audit log entry includes:
+
+ SDK examples above download one chunk. A complete custom exporter must repeat requests until `X-Has-More` is `false`, persist the cursor only after safely writing a chunk, and verify `X-Content-Sha256` against the exact response bytes. Use the CLI when you don't need custom storage or processing.
+
-- `timestamp`
-- `auth_strategy`
-- `user_id`
-- `email`
-- `status`
-- `method`
-- `path`
-- `route`
-- `domain`
-- `duration_ms`
-- `client_ip`
-- `user_agent`
+Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
-See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the full request and response schema.
+See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
diff --git a/reference/cli.mdx b/reference/cli.mdx
index 7a47a9d..b0cc1a3 100644
--- a/reference/cli.mdx
+++ b/reference/cli.mdx
@@ -61,6 +61,9 @@ kernel --version
Create, list, rename, and delete API keys.
+
+ Search and download organization audit logs.
+
## Quick Start
diff --git a/reference/cli/audit-logs.mdx b/reference/cli/audit-logs.mdx
new file mode 100644
index 0000000..eda9825
--- /dev/null
+++ b/reference/cli/audit-logs.mdx
@@ -0,0 +1,93 @@
+---
+title: "Audit Logs"
+---
+
+Search and download [organization audit logs](/info/audit-logs) from the CLI.
+
+## `kernel audit-logs search`
+
+Search audit logs within a time window. Results are ordered newest first.
+
+```bash
+kernel audit-logs search \
+ --start 2026-06-01 \
+ --end 2026-06-08 \
+ --search /browsers \
+ --limit 500 \
+ --output json
+```
+
+If you omit the time flags, the command searches from 24 hours ago through now. The start is inclusive and the end is exclusive. Each time window can cover up to 30 days.
+
+| Flag | Description |
+|---|---|
+| `--start
-## Filter audit logs
-
-The API and SDKs use the same filters for search and export:
-
-- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
-- `service` filters by the service that emitted the audit event.
-- `method` includes one HTTP method.
-- `exclude_method` excludes one or more HTTP methods, with up to 10 values.
-- `search` matches path, user ID, email, client IP, and status.
-- `search_user_id` adds up to 100 user IDs to the free-text search.
-
-
-```typescript TypeScript
-const logs = kernel.auditLogs.list({
- start: '2026-06-01T00:00:00Z',
- end: '2026-06-08T00:00:00Z',
- limit: 50,
- auth_strategy: 'api_key',
- exclude_method: ['GET'],
- search: '/browsers',
-});
-
-for await (const event of logs) {
- console.log(event.email, event.client_ip, event.user_agent);
-}
-```
-
-```python Python
-logs = client.audit_logs.list(
- start="2026-06-01T00:00:00Z",
- end="2026-06-08T00:00:00Z",
- limit=50,
- auth_strategy="api_key",
- exclude_method=["GET"],
- search="/browsers",
-)
-
-for event in logs:
- print(event.email, event.client_ip, event.user_agent)
-```
-
-```go Go
-package main
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/kernel/kernel-go-sdk"
-)
-
-func main() {
- ctx := context.Background()
- client := kernel.NewClient()
-
- pager := client.AuditLogs.ListAutoPaging(ctx, kernel.AuditLogListParams{
- Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
- End: time.Date(2026, time.June, 8, 0, 0, 0, 0, time.UTC),
- Limit: kernel.Int(50),
- AuthStrategy: kernel.String("api_key"),
- ExcludeMethod: []string{"GET"},
- Search: kernel.String("/browsers"),
- })
- for pager.Next() {
- event := pager.Current()
- fmt.Println(event.Email, event.ClientIP, event.UserAgent)
- }
- if err := pager.Err(); err != nil {
- panic(err)
- }
-}
-```
-
-
-## Page with HTTP
+## Search with HTTP
-The REST API returns pagination state in these response headers:
+Search over raw HTTP with a `GET` request. The response carries pagination state in these headers:
- `X-Limit` is the page size applied to the request.
- `X-Has-More` indicates whether older records remain.
@@ -211,14 +154,16 @@ curl --include --get https://api.onkernel.com/audit-logs \
--data-urlencode "start=2026-06-01T00:00:00Z" \
--data-urlencode "end=2026-06-02T00:00:00Z" \
--data-urlencode "limit=100" \
- --data-urlencode "page_token=$NEXT_PAGE_TOKEN"
+ --data-urlencode "page_token="
```
Page tokens are opaque. Don't decode, construct, or modify them.
+See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
+
## Download an export with the CLI
-Use the CLI to download every chunk in a time window into one verified gzip-compressed JSONL file:
+The export workflow is exposed through the `kernel audit-logs download` command and the export-chunk API endpoint. Use the CLI to download every chunk in a time window into one verified gzip-compressed JSONL file:
```bash
kernel audit-logs download \
@@ -227,49 +172,14 @@ kernel audit-logs download \
--to audit-june.jsonl.gz
```
-The CLI:
-
-1. downloads up to 50,000 records per chunk,
-2. verifies each chunk against its `X-Content-Sha256` checksum,
-3. retries transient and integrity failures,
-4. writes to a temporary `.partial` file, and
-5. publishes the final file only after every chunk succeeds.
-
-Incomplete downloads are removed and don't resume across CLI runs. Rerunning the command starts the export again. Use `--force` to replace an existing output file.
+The download is all-or-nothing: the CLI verifies every chunk it receives and writes the output file only after the whole export succeeds. Incomplete downloads are removed and don't resume across CLI runs; rerunning the command starts the export again. Use `--force` to replace an existing output file. See the [audit log CLI reference](/reference/cli/audit-logs#download-behavior) for chunk sizes, checksums, and retry behavior.
Like CLI search, CLI download excludes `GET` by default. Search and download accept the same filters.
-## Download an export chunk with HTTP
-
-The export API returns one chunk per request. Save the response headers separately from the binary body:
-
-```bash
-curl --fail-with-body --silent --show-error --get \
- https://api.onkernel.com/audit-logs/export/chunk \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "format=jsonl.gz" \
- --dump-header audit-logs-chunk.headers \
- --output audit-logs-chunk.jsonl.gz
-```
-
-When `X-Has-More` is `true`, copy `X-Next-Cursor` and pass it unchanged to download the next chunk:
-
-```bash
-curl --fail-with-body --silent --show-error --get \
- https://api.onkernel.com/audit-logs/export/chunk \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "format=jsonl.gz" \
- --data-urlencode "cursor=$NEXT_CURSOR" \
- --dump-header audit-logs-next-chunk.headers \
- --output audit-logs-next-chunk.jsonl.gz
-```
-
## Download an export chunk with an SDK
+The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
+
Save the response body exactly as returned, then inspect `X-Has-More`. When it is `true`, pass `X-Next-Cursor` as `cursor` on the next request.
@@ -358,10 +268,39 @@ func main() {
```
+## Download an export chunk with HTTP
+
+Save the response headers separately from the binary body:
+
+```bash
+curl --fail-with-body --silent --show-error --get \
+ https://api.onkernel.com/audit-logs/export/chunk \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "format=jsonl.gz" \
+ --dump-header audit-logs-chunk.headers \
+ --output audit-logs-chunk.jsonl.gz
+```
+
+When `X-Has-More` is `true`, copy `X-Next-Cursor` and pass it unchanged to download the next chunk:
+
+```bash
+curl --fail-with-body --silent --show-error --get \
+ https://api.onkernel.com/audit-logs/export/chunk \
+ --header "Authorization: Bearer $KERNEL_API_KEY" \
+ --data-urlencode "start=2026-06-01T00:00:00Z" \
+ --data-urlencode "end=2026-06-02T00:00:00Z" \
+ --data-urlencode "format=jsonl.gz" \
+ --data-urlencode "cursor=" \
+ --dump-header audit-logs-next-chunk.headers \
+ --output audit-logs-next-chunk.jsonl.gz
+```
+
- SDK examples above download one chunk. A complete custom exporter must repeat requests until `X-Has-More` is `false`, persist the cursor only after safely writing a chunk, and verify `X-Content-Sha256` against the exact response bytes. Use the CLI when you don't need custom storage or processing.
+ The SDK and HTTP examples above download one chunk. A complete custom exporter must repeat requests until `X-Has-More` is `false`, persist the cursor only after safely writing a chunk, and verify `X-Content-Sha256` against the exact response bytes. Use the CLI when you don't need custom storage or processing.
Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
-See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
+See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for the complete export contract and response schema.
diff --git a/reference/cli/audit-logs.mdx b/reference/cli/audit-logs.mdx
index eda9825..e8c037d 100644
--- a/reference/cli/audit-logs.mdx
+++ b/reference/cli/audit-logs.mdx
@@ -20,11 +20,11 @@ kernel audit-logs search \
If you omit the time flags, the command searches from 24 hours ago through now. The start is inclusive and the end is exclusive. Each time window can cover up to 30 days.
| Flag | Description |
-|---|---|
+|------|-------------|
| `--start ` | Inclusive start, as RFC 3339 or `YYYY-MM-DD`. Defaults to 24 hours ago. |
| `--end ` | Exclusive end, as RFC 3339 or `YYYY-MM-DD`. Defaults to now. |
| `--search ` | Search path, user ID, email, client IP, and status. |
-| `--method ` | Return one HTTP method. Passing `GET` also overrides the default GET exclusion. |
+| `--method ` | Return one HTTP method. Passing any method disables the default GET exclusion. |
| `--exclude-method ` | Exclude an HTTP method in addition to the default GET exclusion. |
| `--include-get` | Include GET requests, which are excluded by default. |
| `--service ` | Filter by service. |
@@ -63,11 +63,11 @@ kernel audit-logs download \
`--start` and `--end` are required. The start is inclusive and the end is exclusive, and the time window can cover up to 30 days.
| Flag | Description |
-|---|---|
+|------|-------------|
| `--start ` | Inclusive start, as RFC 3339 or `YYYY-MM-DD`. Required. |
| `--end ` | Exclusive end, as RFC 3339 or `YYYY-MM-DD`. Required. |
| `--search ` | Search path, user ID, email, client IP, and status. |
-| `--method ` | Return one HTTP method. Passing `GET` also overrides the default GET exclusion. |
+| `--method ` | Return one HTTP method. Passing any method disables the default GET exclusion. |
| `--exclude-method ` | Exclude an HTTP method in addition to the default GET exclusion. |
| `--include-get` | Include GET requests, which are excluded by default. |
| `--service ` | Filter by service. |
From 85eb4621d79bac15072bb1eb67233fd82ba2ba07 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Thu, 16 Jul 2026 21:13:53 +0000
Subject: [PATCH 07/19] Drop Before you start section from audit log guide
---
info/audit-logs.mdx | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 61017e6..0f31e5d 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -3,7 +3,7 @@ title: "Audit Logs"
description: "Search and export organization API request audit events"
---
-Audit logs record authenticated API requests for your organization. Use them to review who called Kernel, which endpoint they called, when the request happened, and how the request completed.
+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.
Choose the workflow that matches the amount of data you need:
@@ -14,11 +14,6 @@ Choose the workflow that matches the amount of data you need:
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.
-## Before you start
-
-- Audit logs cover your entire organization, not a single project.
-- Set `KERNEL_API_KEY` before running the examples. Any credential that authenticates to your organization can read its audit logs.
-
## Filter audit logs
The API and SDKs use the same filters for search and export:
From 8ffb09c0a5ced17dc62d0dfd3f99d8a7e38b8107 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Fri, 17 Jul 2026 14:02:00 +0000
Subject: [PATCH 08/19] Show complete SDK exporter loop for audit log chunks
---
info/audit-logs.mdx | 105 +++++++++++++++++++++++++-------------------
1 file changed, 60 insertions(+), 45 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 0f31e5d..0b4bd24 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -175,31 +175,37 @@ Like CLI search, CLI download excludes `GET` by default. Search and download acc
The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
-Save the response body exactly as returned, then inspect `X-Has-More`. When it is `true`, pass `X-Next-Cursor` as `cursor` on the next request.
+Each chunk is a complete gzip stream, so appending each response body as-is produces one valid file. Repeat requests until `X-Has-More` is `false`, passing `X-Next-Cursor` back as `cursor`:
```typescript TypeScript
-import { writeFile } from 'node:fs/promises';
+import { appendFile } from 'node:fs/promises';
import Kernel from '@onkernel/sdk';
const kernel = new Kernel({
apiKey: process.env.KERNEL_API_KEY,
});
-const response = await kernel.auditLogs.exportChunk({
- start: '2026-06-01T00:00:00Z',
- end: '2026-06-02T00:00:00Z',
- format: 'jsonl.gz',
- exclude_method: ['GET'],
-});
-
-await writeFile(
- 'audit-logs-chunk.jsonl.gz',
- Buffer.from(await response.arrayBuffer()),
-);
-
-console.log(response.headers.get('x-has-more'));
-console.log(response.headers.get('x-next-cursor'));
+let cursor: string | undefined;
+while (true) {
+ const response = await kernel.auditLogs.exportChunk({
+ start: '2026-06-01T00:00:00Z',
+ end: '2026-06-02T00:00:00Z',
+ format: 'jsonl.gz',
+ exclude_method: ['GET'],
+ cursor,
+ });
+
+ await appendFile(
+ 'audit-logs.jsonl.gz',
+ Buffer.from(await response.arrayBuffer()),
+ );
+
+ if (response.headers.get('x-has-more') !== 'true') {
+ break;
+ }
+ cursor = response.headers.get('x-next-cursor') ?? undefined;
+}
```
```python Python
@@ -208,16 +214,20 @@ from kernel import Kernel
client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
-response = client.audit_logs.export_chunk(
- start="2026-06-01T00:00:00Z",
- end="2026-06-02T00:00:00Z",
- format="jsonl.gz",
- exclude_method=["GET"],
-)
-response.write_to_file("audit-logs-chunk.jsonl.gz")
+params = {
+ "start": "2026-06-01T00:00:00Z",
+ "end": "2026-06-02T00:00:00Z",
+ "format": "jsonl.gz",
+ "exclude_method": ["GET"],
+}
-print(response.http_response.headers.get("x-has-more"))
-print(response.http_response.headers.get("x-next-cursor"))
+with open("audit-logs.jsonl.gz", "wb") as file:
+ while True:
+ response = client.audit_logs.export_chunk(**params)
+ file.write(response.read())
+ if response.http_response.headers.get("x-has-more") != "true":
+ break
+ params["cursor"] = response.http_response.headers.get("x-next-cursor")
```
```go Go
@@ -236,33 +246,42 @@ func main() {
ctx := context.Background()
client := kernel.NewClient()
- response, err := client.AuditLogs.ExportChunk(ctx, kernel.AuditLogExportChunkParams{
- Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
- End: time.Date(2026, time.June, 2, 0, 0, 0, 0, time.UTC),
- Format: kernel.AuditLogExportChunkParamsFormatJSONLGz,
- ExcludeMethod: []string{"GET"},
- })
- if err != nil {
- panic(err)
- }
- defer response.Body.Close()
-
- file, err := os.Create("audit-logs-chunk.jsonl.gz")
+ file, err := os.Create("audit-logs.jsonl.gz")
if err != nil {
panic(err)
}
defer file.Close()
- if _, err := io.Copy(file, response.Body); err != nil {
- panic(err)
+ params := kernel.AuditLogExportChunkParams{
+ Start: time.Date(2026, time.June, 1, 0, 0, 0, 0, time.UTC),
+ End: time.Date(2026, time.June, 2, 0, 0, 0, 0, time.UTC),
+ Format: kernel.AuditLogExportChunkParamsFormatJSONLGz,
+ ExcludeMethod: []string{"GET"},
}
- println(response.Header.Get("X-Has-More"))
- println(response.Header.Get("X-Next-Cursor"))
+ for {
+ response, err := client.AuditLogs.ExportChunk(ctx, params)
+ if err != nil {
+ panic(err)
+ }
+ if _, err := io.Copy(file, response.Body); err != nil {
+ panic(err)
+ }
+ response.Body.Close()
+
+ if response.Header.Get("X-Has-More") != "true" {
+ break
+ }
+ params.Cursor = kernel.String(response.Header.Get("X-Next-Cursor"))
+ }
}
```
+
+ A production exporter should persist the cursor only after safely writing a chunk and verify `X-Content-Sha256` against the exact response bytes.
+
+
## Download an export chunk with HTTP
Save the response headers separately from the binary body:
@@ -292,10 +311,6 @@ curl --fail-with-body --silent --show-error --get \
--output audit-logs-next-chunk.jsonl.gz
```
-
- The SDK and HTTP examples above download one chunk. A complete custom exporter must repeat requests until `X-Has-More` is `false`, persist the cursor only after safely writing a chunk, and verify `X-Content-Sha256` against the exact response bytes. Use the CLI when you don't need custom storage or processing.
-
-
Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for the complete export contract and response schema.
From ea3c52146134b7777e726bea986e2fd116cdc032 Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Fri, 17 Jul 2026 14:06:08 +0000
Subject: [PATCH 09/19] Move CLI usage out of audit log guide to CLI reference
---
info/audit-logs.mdx | 52 +++++----------------------------------------
1 file changed, 5 insertions(+), 47 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 0b4bd24..216bd95 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -9,11 +9,13 @@ Choose the workflow that matches the amount of data you need:
| Workflow | Best for | Output |
|----------|----------|--------|
-| Search | Interactive investigation and recent activity | A table, JSON array, or paginated SDK results |
+| Search | Interactive investigation and recent activity | Paginated JSON events |
| Export | Archival, compliance, and offline analysis | JSON Lines (`.jsonl`) or gzip-compressed JSON Lines (`.jsonl.gz`) |
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.
+Working from the terminal? Both workflows are also available in the CLI — see the [audit log CLI reference](/reference/cli/audit-logs).
+
## Filter audit logs
The API and SDKs use the same filters for search and export:
@@ -25,35 +27,6 @@ The API and SDKs use the same filters for search and export:
- `search` matches path, user ID, email, client IP, and status.
- `search_user_id` adds up to 100 user IDs to the free-text search.
-The CLI exposes the same filters as flags. See the [audit log CLI reference](/reference/cli/audit-logs) for the full list.
-
-## Search with the CLI
-
-Search the previous 24 hours:
-
-```bash
-kernel audit-logs search
-```
-
-Add a time window and filters:
-
-```bash
-kernel audit-logs search \
- --start 2026-06-01 \
- --end 2026-06-08 \
- --search /browsers \
- --limit 500 \
- --output json
-```
-
-The CLI accepts RFC 3339 timestamps or `YYYY-MM-DD` dates. Date-only values represent midnight UTC.
-
-
- The CLI excludes `GET` requests by default to reduce noise. Pass `--include-get` to include them, or `--method GET` to return only `GET` requests. The API and SDKs don't exclude `GET` unless you set `exclude_method`.
-
-
-See the [audit log CLI reference](/reference/cli/audit-logs) for all flags and defaults.
-
## Search with an SDK
Each API page contains up to 100 events. The SDK pagination helpers request older pages as you iterate.
@@ -156,22 +129,7 @@ Page tokens are opaque. Don't decode, construct, or modify them.
See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
-## Download an export with the CLI
-
-The export workflow is exposed through the `kernel audit-logs download` command and the export-chunk API endpoint. Use the CLI to download every chunk in a time window into one verified gzip-compressed JSONL file:
-
-```bash
-kernel audit-logs download \
- --start 2026-06-01 \
- --end 2026-07-01 \
- --to audit-june.jsonl.gz
-```
-
-The download is all-or-nothing: the CLI verifies every chunk it receives and writes the output file only after the whole export succeeds. Incomplete downloads are removed and don't resume across CLI runs; rerunning the command starts the export again. Use `--force` to replace an existing output file. See the [audit log CLI reference](/reference/cli/audit-logs#download-behavior) for chunk sizes, checksums, and retry behavior.
-
-Like CLI search, CLI download excludes `GET` by default. Search and download accept the same filters.
-
-## Download an export chunk with an SDK
+## Export with an SDK
The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
@@ -282,7 +240,7 @@ func main() {
A production exporter should persist the cursor only after safely writing a chunk and verify `X-Content-Sha256` against the exact response bytes.
-## Download an export chunk with HTTP
+## Export with HTTP
Save the response headers separately from the binary body:
From 5433c1b8a3a6cfa905a0aeea9949a23177fc6dcc Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Fri, 17 Jul 2026 14:07:30 +0000
Subject: [PATCH 10/19] Move raw HTTP examples out of audit log guide to API
reference
---
info/audit-logs.mdx | 66 +++------------------------------------------
1 file changed, 3 insertions(+), 63 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 216bd95..abfc3b1 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -14,7 +14,7 @@ Choose the workflow that matches the amount of data you need:
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.
-Working from the terminal? Both workflows are also available in the CLI — see the [audit log CLI reference](/reference/cli/audit-logs).
+Both workflows are also available in the [CLI](/reference/cli/audit-logs), and the API reference documents the raw HTTP contract for [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).
## Filter audit logs
@@ -27,7 +27,7 @@ The API and SDKs use the same filters for search and export:
- `search` matches path, user ID, email, client IP, and status.
- `search_user_id` adds up to 100 user IDs to the free-text search.
-## Search with an SDK
+## Search audit logs
Each API page contains up to 100 events. The SDK pagination helpers request older pages as you iterate.
@@ -96,40 +96,9 @@ func main() {
```
-## Search with HTTP
-
-Search over raw HTTP with a `GET` request. The response carries pagination state in these headers:
-
-- `X-Limit` is the page size applied to the request.
-- `X-Has-More` indicates whether older records remain.
-- `X-Next-Page-Token` contains the opaque token for the next page.
-
-Request the first page:
-
-```bash
-curl --include --get https://api.onkernel.com/audit-logs \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "limit=100"
-```
-
-If `X-Has-More` is `true`, copy the `X-Next-Page-Token` value and pass it unchanged:
-
-```bash
-curl --include --get https://api.onkernel.com/audit-logs \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "limit=100" \
- --data-urlencode "page_token="
-```
-
-Page tokens are opaque. Don't decode, construct, or modify them.
-
See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
-## Export with an SDK
+## Export audit logs
The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
@@ -240,35 +209,6 @@ func main() {
A production exporter should persist the cursor only after safely writing a chunk and verify `X-Content-Sha256` against the exact response bytes.
-## Export with HTTP
-
-Save the response headers separately from the binary body:
-
-```bash
-curl --fail-with-body --silent --show-error --get \
- https://api.onkernel.com/audit-logs/export/chunk \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "format=jsonl.gz" \
- --dump-header audit-logs-chunk.headers \
- --output audit-logs-chunk.jsonl.gz
-```
-
-When `X-Has-More` is `true`, copy `X-Next-Cursor` and pass it unchanged to download the next chunk:
-
-```bash
-curl --fail-with-body --silent --show-error --get \
- https://api.onkernel.com/audit-logs/export/chunk \
- --header "Authorization: Bearer $KERNEL_API_KEY" \
- --data-urlencode "start=2026-06-01T00:00:00Z" \
- --data-urlencode "end=2026-06-02T00:00:00Z" \
- --data-urlencode "format=jsonl.gz" \
- --data-urlencode "cursor=" \
- --dump-header audit-logs-next-chunk.headers \
- --output audit-logs-next-chunk.jsonl.gz
-```
-
Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for the complete export contract and response schema.
From c58722a5ec856f347dfdc000936d868968dfb1db Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Fri, 17 Jul 2026 14:11:53 +0000
Subject: [PATCH 11/19] docs(audit-logs): align SDK, CLI, and API descriptions
with existing docs patterns
- Use full request/response schema phrasing consistent with projects and other info pages
- Clarify CLI vs API/SDK default GET exclusion in filter docs
- Link CLI and API reference in intro with consistent wording
- Fix CLI flag output description to 'Output raw JSON array' to match browsers, proxies, profiles, api-keys reference pages
- Note export cursor retry and checksum verification matches CLI download behavior
---
info/audit-logs.mdx | 16 ++++++++++------
reference/cli/audit-logs.mdx | 2 +-
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index abfc3b1..0313cbd 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -14,7 +14,7 @@ Choose the workflow that matches the amount of data you need:
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.
-Both workflows are also available in the [CLI](/reference/cli/audit-logs), and the API reference documents the raw HTTP contract for [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).
+Both workflows 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.
## Filter audit logs
@@ -22,11 +22,15 @@ The API and SDKs use the same filters for search and export:
- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
- `service` filters by the service that emitted the audit event.
-- `method` includes one HTTP method.
-- `exclude_method` excludes one or more HTTP methods, with up to 10 values.
+- `method` includes one HTTP method. In the CLI, passing any method disables the default GET exclusion.
+- `exclude_method` excludes one or more HTTP methods in addition to the default CLI exclusion, with up to 10 values.
- `search` matches path, user ID, email, client IP, and status.
- `search_user_id` adds up to 100 user IDs to the free-text search.
+
+The CLI excludes `GET` by default to reduce noise; the API and SDKs include all methods unless you filter. Use `--include-get` in the CLI or omit `exclude_method: ["GET"]` in SDKs to include them.
+
+
## Search audit logs
Each API page contains up to 100 events. The SDK pagination helpers request older pages as you iterate.
@@ -96,13 +100,13 @@ func main() {
```
-See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-audit-logs) for the complete search contract and response schema.
+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
The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
-Each chunk is a complete gzip stream, so appending each response body as-is produces one valid file. Repeat requests until `X-Has-More` is `false`, passing `X-Next-Cursor` back as `cursor`:
+Each chunk is a complete gzip stream, so appending each response body as-is produces one valid file. Repeat requests until `X-Has-More` is `false`, passing `X-Next-Cursor` back as `cursor`. This matches the CLI download behavior, which verifies `X-Content-Sha256` and retries transient failures:
```typescript TypeScript
@@ -211,4 +215,4 @@ func main() {
Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
-See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for the complete export contract and response schema.
+See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/download-an-audit-log-export-chunk) for the full request and response schema.
diff --git a/reference/cli/audit-logs.mdx b/reference/cli/audit-logs.mdx
index e8c037d..7727a73 100644
--- a/reference/cli/audit-logs.mdx
+++ b/reference/cli/audit-logs.mdx
@@ -31,7 +31,7 @@ If you omit the time flags, the command searches from 24 hours ago through now.
| `--auth-strategy ` | Filter by authentication strategy. |
| `--user-id ` | Add a user ID to the search. Repeat the flag for multiple IDs. |
| `--limit ` | Maximum total number of results. Defaults to `100`. |
-| `--output json`, `-o json` | Return a JSON array instead of a table. |
+| `--output json`, `-o json` | Output raw JSON array. |
`--limit` controls the total number of CLI results. The CLI automatically requests API pages of up to 100 records until it reaches that limit or runs out of results.
From e706b47adb20b723a2b12fe532c7b2af2ed01f1a Mon Sep 17 00:00:00 2001
From: yummybomb <19238148+yummybomb@users.noreply.github.com>
Date: Fri, 17 Jul 2026 14:30:19 +0000
Subject: [PATCH 12/19] Clarify audit log export safety
---
info/audit-logs.mdx | 66 ++++++++++++++++++++----------------
reference/cli/audit-logs.mdx | 4 +--
2 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/info/audit-logs.mdx b/info/audit-logs.mdx
index 0313cbd..f16c852 100644
--- a/info/audit-logs.mdx
+++ b/info/audit-logs.mdx
@@ -22,13 +22,13 @@ The API and SDKs use the same filters for search and export:
- `auth_strategy` filters by authentication method, such as `api_key`, `dashboard`, or `oauth`.
- `service` filters by the service that emitted the audit event.
-- `method` includes one HTTP method. In the CLI, passing any method disables the default GET exclusion.
-- `exclude_method` excludes one or more HTTP methods in addition to the default CLI exclusion, with up to 10 values.
+- `method` includes one HTTP method.
+- `exclude_method` excludes up to 10 HTTP methods.
- `search` matches path, user ID, email, client IP, and status.
- `search_user_id` adds up to 100 user IDs to the free-text search.
-The CLI excludes `GET` by default to reduce noise; the API and SDKs include all methods unless you filter. Use `--include-get` in the CLI or omit `exclude_method: ["GET"]` in SDKs to include them.
+The API and SDKs include all methods unless you filter. Only the CLI excludes `GET` by default to reduce noise. Pass `--include-get` to include GET requests, or pass `--method GET` to return only GET requests.
## Search audit logs
@@ -106,36 +106,40 @@ See the [API reference](https://kernel.sh/docs/api-reference/audit-logs/list-aud
The export API returns one chunk per request. Export paging uses a cursor rather than the page token used by search; both are opaque values you pass back unchanged.
-Each chunk is a complete gzip stream, so appending each response body as-is produces one valid file. Repeat requests until `X-Has-More` is `false`, passing `X-Next-Cursor` back as `cursor`. This matches the CLI download behavior, which verifies `X-Content-Sha256` and retries transient failures:
+Repeat requests until `X-Has-More` is `false`, passing `X-Next-Cursor` back as `cursor`. With the `jsonl.gz` format, each chunk is an independent gzip member, and appending the members produces a valid gzip file. With `jsonl`, each chunk contains raw JSON Lines that you can also append.
+
+The following minimal examples write all chunks to one file. For a hardened export with checksum verification and retries, use the [CLI](/reference/cli/audit-logs#kernel-audit-logs-download).
```typescript TypeScript
-import { appendFile } from 'node:fs/promises';
+import { open } from 'node:fs/promises';
import Kernel from '@onkernel/sdk';
const kernel = new Kernel({
apiKey: process.env.KERNEL_API_KEY,
});
-let cursor: string | undefined;
-while (true) {
- const response = await kernel.auditLogs.exportChunk({
- start: '2026-06-01T00:00:00Z',
- end: '2026-06-02T00:00:00Z',
- format: 'jsonl.gz',
- exclude_method: ['GET'],
- cursor,
- });
-
- await appendFile(
- 'audit-logs.jsonl.gz',
- Buffer.from(await response.arrayBuffer()),
- );
-
- if (response.headers.get('x-has-more') !== 'true') {
- break;
+const file = await open('audit-logs.jsonl.gz', 'w');
+try {
+ let cursor: string | undefined;
+ while (true) {
+ const response = await kernel.auditLogs.exportChunk({
+ start: '2026-06-01T00:00:00Z',
+ end: '2026-06-02T00:00:00Z',
+ format: 'jsonl.gz',
+ exclude_method: ['GET'],
+ cursor,
+ });
+
+ await file.write(Buffer.from(await response.arrayBuffer()));
+
+ if (response.headers.get('x-has-more') !== 'true') {
+ break;
+ }
+ cursor = response.headers.get('x-next-cursor') ?? undefined;
}
- cursor = response.headers.get('x-next-cursor') ?? undefined;
+} finally {
+ await file.close();
}
```
@@ -156,9 +160,9 @@ with open("audit-logs.jsonl.gz", "wb") as file:
while True:
response = client.audit_logs.export_chunk(**params)
file.write(response.read())
- if response.http_response.headers.get("x-has-more") != "true":
+ if response.headers.get("x-has-more") != "true":
break
- params["cursor"] = response.http_response.headers.get("x-next-cursor")
+ params["cursor"] = response.headers.get("x-next-cursor")
```
```go Go
@@ -195,10 +199,14 @@ func main() {
if err != nil {
panic(err)
}
- if _, err := io.Copy(file, response.Body); err != nil {
- panic(err)
+ _, copyErr := io.Copy(file, response.Body)
+ closeErr := response.Body.Close()
+ if copyErr != nil {
+ panic(copyErr)
+ }
+ if closeErr != nil {
+ panic(closeErr)
}
- response.Body.Close()
if response.Header.Get("X-Has-More") != "true" {
break
@@ -210,7 +218,7 @@ func main() {
- A production exporter should persist the cursor only after safely writing a chunk and verify `X-Content-Sha256` against the exact response bytes.
+ Don't use these minimal loops for a production export without adding integrity checks and durable cursor storage. For each chunk, buffer the exact response bytes, compare their SHA-256 hash with `X-Content-Sha256`, write the verified bytes, and then validate the cursor. When `X-Has-More` is `true`, require a non-empty `X-Next-Cursor` that differs from the current cursor. Persist that cursor only after the verified chunk is safely written. You must also retry transient failures. The [CLI download command](/reference/cli/audit-logs#kernel-audit-logs-download) implements this hardened path.
Export chunks contain one JSON object per line. They use the same fields as search results and add `event_id`, which provides a stable tie-breaker when multiple events share a timestamp.
diff --git a/reference/cli/audit-logs.mdx b/reference/cli/audit-logs.mdx
index 7727a73..275c85c 100644
--- a/reference/cli/audit-logs.mdx
+++ b/reference/cli/audit-logs.mdx
@@ -51,7 +51,7 @@ kernel audit-logs search --method GET
## `kernel audit-logs download`
-Download every audit log in a time window as one gzip-compressed JSONL file.
+Download matching audit logs in a time window as one gzip-compressed JSONL file.
```bash
kernel audit-logs download \
@@ -84,7 +84,7 @@ The command downloads up to 50,000 records at a time and appends each chunk to `
A chunk is attempted up to seven times when a network error, HTTP `429`, HTTP `5xx`, truncated response, or checksum mismatch occurs. Retries use exponential backoff capped at eight seconds.
-The CLI renames the temporary file to the requested output only after every chunk succeeds. If the download fails, it removes the incomplete temporary file. Downloads don't resume across CLI runs; rerunning the command starts again from the beginning.
+The CLI renames the temporary file to the requested output only after every chunk transfers successfully. If a transfer fails, it removes the incomplete temporary file. If the final rename fails, the completed download remains at `