Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1425136
CLI: Update SDK to 0.86.0 and close list-endpoint flag coverage gaps
kernel-internal[bot] Aug 4, 2026
b5df240
CLI: Update Go SDK to b94cd58
kernel-internal[bot] Aug 4, 2026
2585945
CLI: Verify SDK coverage at 9e39767 and add audit-logs download --format
kernel-internal[bot] Aug 4, 2026
1d24d11
CLI: Update Go SDK to f026272
kernel-internal[bot] Aug 5, 2026
9f4d3fd
CLI: Update Go SDK to f607274
kernel-internal[bot] Aug 5, 2026
eaf3d8a
CLI: Update Go SDK to a42a4e3 and add telemetry OTLP export flags
kernel-internal[bot] Aug 6, 2026
b4c161b
CLI: Update Go SDK to 10e64888bbba7139d815dde7eec7cfa895093aa1
kernel-internal[bot] Aug 6, 2026
909c48a
CLI: Update Go SDK to f64bbb81fe7cf5d5738264af775bd853f911734a
kernel-internal[bot] Aug 6, 2026
896f18a
CLI: Update Go SDK to 05231c85962558ae3d4681646697ab7175d91189
kernel-internal[bot] Aug 6, 2026
2c11de1
CLI: Update Go SDK to 2bc9fe850fb46a37807363711ee8503e849a8235
kernel-internal[bot] Aug 6, 2026
2f66bf8
CLI: Update Go SDK to v0.86.1 (4511ca3)
kernel-internal[bot] Aug 6, 2026
3b322e7
CLI: Update Go SDK to b6de398 and surface profile_save_changes
kernel-internal[bot] Aug 7, 2026
db38834
Merge main into cli-coverage-update
kernel-internal[bot] Aug 7, 2026
3f4bd30
chore: Update Go SDK to 6d7dc2e
kernel-internal[bot] Aug 7, 2026
69d0fe1
CLI: Update Go SDK to d44daa9 and add `kernel auth context`
kernel-internal[bot] Aug 7, 2026
c38692a
CLI: Update Go SDK to 654fc1c
kernel-internal[bot] Aug 8, 2026
bc24ff8
CLI: Update Go SDK to v0.87.0 (3d0249f)
kernel-internal[bot] Aug 8, 2026
87b04e8
CLI: Update Go SDK to 4e924df and add proxy configuration flags
kernel-internal[bot] Aug 8, 2026
b609b4e
CLI: Update Go SDK to cd3e117
kernel-internal[bot] Aug 9, 2026
892c0a6
Merge main into cli-coverage-update
kernel-internal[bot] Aug 10, 2026
3d3a508
CLI: Update Go SDK to 5819723
kernel-internal[bot] Aug 10, 2026
c187d67
CLI: Update Go SDK to d21e6b3 (not d2577f5 -- see below)
kernel-internal[bot] Aug 10, 2026
0dd5edd
CLI: Update Go SDK to 5882994 and add projects list filters
kernel-internal[bot] Aug 10, 2026
8fa6802
CLI: Update Go SDK to v0.88.0 (7e9d5f1)
kernel-internal[bot] Aug 10, 2026
9c617bd
CLI: Update Go SDK to 1b7bde8
kernel-internal[bot] Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,16 @@ Commands with JSON output support:
- `-H, --headless` - Launch browser without GUI access
- `--kiosk` - Launch browser in kiosk mode
- `--start-url <url>` - Initial page to open on launch
- `--proxy-id <id>` / `--proxy-name <name>` - Use that proxy for the session regardless of stealth (mutually exclusive with each other and with `--proxy-mode`)
- `--proxy-mode direct|default` - Egress mode instead of a selected proxy: `direct` for no proxy regardless of stealth, `default` for the stealth-derived default (Kernel's stealth proxy with `--stealth`, direct egress otherwise). Omit all proxy flags to get the default.
- `--name <name>` - Optional unique name for the session (used to find it later by name; can be changed with `browsers update --name`)
- `--tag <KEY=VALUE>` - Set a tag on the session, repeatable; up to 50 pairs
- `--pool-id <id>` - Acquire a browser from the specified pool (mutually exclusive with --pool-name; ignores other session flags). `--name`/`--tag` still apply to the acquired session.
- `--pool-name <name>` - Acquire a browser from the pool name (mutually exclusive with --pool-id; ignores other session flags)
- `--telemetry=all` - Enable telemetry for all categories
- `--telemetry=off` - Disable telemetry
- `--telemetry=<list>` - Per-category config, e.g. `--telemetry=network=on,page=off`
- `--telemetry-export-otlp <id-or-name>` - Export captured telemetry over OTLP to one of the org's configured destinations. Implies `--telemetry=all` when `--telemetry` is not set, since export requires capture. Use `--telemetry-export-otlp=off` to disable export.
- `--chrome-policy <json>` - Custom Chrome enterprise policy as a JSON object. Kernel-managed policies (extensions, proxy, automation) are rejected server-side.
- `--chrome-policy-file <path>` - Read the Chrome enterprise policy from a file (use `-` for stdin). Mutually exclusive with `--chrome-policy`.
- `--output json`, `-o json` - Output raw JSON object
Expand All @@ -241,7 +244,10 @@ Commands with JSON output support:
- `--telemetry=all` - Enable telemetry for all categories
- `--telemetry=off` - Disable telemetry
- `--telemetry=<list>` - Per-category config, e.g. `--telemetry=network=on,page=off`
- `--disable-default-proxy` - Disable the default stealth proxy so the browser connects directly; use `--disable-default-proxy=false` to re-enable it
- `--proxy-id <id>` / `--proxy-name <name>` - Switch the session to that proxy regardless of stealth (mutually exclusive with each other and with `--proxy-mode`)
- `--proxy-mode direct|default` - Change egress mode: `direct` for no proxy regardless of stealth, `default` to restore the browser default after using a selected proxy. Changing the proxy does not change stealth or CAPTCHA solver behavior.
- `--clear-proxy` - Drop the selected proxy and restore the browser default (same as `--proxy-mode=default`)
- `--disable-default-proxy` - Connect directly instead of through the default stealth proxy (same as `--proxy-mode=direct`); use `--disable-default-proxy=false` to restore the default
- `--output json`, `-o json` - Output raw JSON object
- `kernel browsers curl <id> <url>` - Make HTTP requests through a browser session's Chrome network stack
- `-X, --request <method>` - HTTP method (default: GET; defaults to POST when `--data` is set)
Expand Down Expand Up @@ -321,6 +327,16 @@ Telemetry config is a sub-field of the browser session. Use `browsers create` or

Per-category updates are partial — only categories you name are changed; others retain their current state. `--telemetry=all` and `--telemetry=off` reset the entire config.

#### Exporting telemetry

Captured telemetry can be exported over OTLP to one of the org's configured destinations with `--telemetry-export-otlp <id-or-name>`. A value that looks like an ID is sent as one; anything else is resolved as a destination name, which must match exactly one destination in the org.

- Capture and export: `kernel browsers create --telemetry-export-otlp my-collector`
- Capture without exporting: `kernel browsers create --telemetry=all`
- Stop exporting: `--telemetry-export-otlp=off`

Export is bound at session creation, so it is available on `browsers create` and on the managed-auth commands that create a browser (`auth connections create`, `update`, and `login`). A browser session keeps the destination it was created with — `browsers update` cannot change it — and browser pools do not support export.

- `kernel browsers telemetry stream <id>` - Stream live telemetry events (NDJSON with `-o json`)
- `--categories <list>` - Filter by event category (`console`, `network`, `page`, `interaction`, `control`, `connection`, `system`, `screenshot`, `captcha`, `monitor`)
- `--types <list>` - Filter by event type (e.g. `network_response`, `console_error`)
Expand Down Expand Up @@ -534,6 +550,11 @@ Per-category updates are partial — only categories you name are changed; other
- `kernel proxies delete <id>` - Delete a proxy configuration
- `-y, --yes` - Skip confirmation prompt

### Auth Context

- `kernel auth context` - Show the identity and authorization context resolved for the current credentials: the authenticated principal, organization, credential scope, and the effective scope for the request. Credential secrets are never returned. Pass `--project <id>` to see the effective scope a project-scoped request would get.
- `--output json`, `-o json` - Output raw JSON object

### Auth Connections

Managed auth connections (`kernel auth connections`). The commands below are new or gained new flags; run `kernel auth connections --help` for the full command list.
Expand All @@ -543,12 +564,21 @@ Managed auth connections (`kernel auth connections`). The commands below are new
- `--page <n>` - Page number (1-based, default: 1)
- `--per-page <n>` - Items per page (default: 20)
- `--output json`, `-o json` - Output raw JSON array
- `kernel auth connections create` - New flag:
- `kernel auth connections create` - New flags:
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy configuration for this connection's login, reauth, and health-check browser sessions (mutually exclusive). Omit to derive the default from stealth.
- `--stealth` - Whether those browser sessions run in stealth mode (default: true); use `--stealth=false` to disable
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Default telemetry for this connection's browser sessions. Same semantics as `kernel browsers create`
- `kernel auth connections update <id>` - New flag:
- `--telemetry-export-otlp <id-or-name>` - Export this connection's captured telemetry over OTLP to one of the org's configured destinations. Implies `--telemetry=all` when `--telemetry` is not set. Use `=off` to disable export.
- `kernel auth connections update <id>` - New flags:
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy configuration for future browser sessions (mutually exclusive). Use `--proxy-mode=default` to drop a selected proxy rather than passing an empty value.
- `--stealth` - Set whether future browser sessions run in stealth mode; use `--stealth=false` to disable
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Update telemetry for future browser sessions
- `kernel auth connections login <id>` - New flag:
- `--telemetry-export-otlp <id-or-name>` - Update where future sessions export captured telemetry. Naming a destination requires passing `--telemetry` in the same command, since the API validates capture and export together and enabling capture here would replace the connection's current category selection. Use `=off` to disable export.
- `kernel auth connections login <id>` - New flags:
- `--proxy-id <id>` / `--proxy-name <name>` / `--proxy-mode direct|default` - Proxy override for this login's browser session (mutually exclusive); omitted properties inherit the connection defaults
- `--stealth` - Stealth override for this login's browser session; use `--stealth=false` to disable
- `--telemetry=all` / `--telemetry=off` / `--telemetry=<categories>` - Telemetry override for this login only, merged onto the connection's config
- `--telemetry-export-otlp <id-or-name>` - Export override for this login only. Naming a destination requires passing `--telemetry` in the same command. Use `=off` to disable export.
- `kernel auth connections submit <id>` - New flags:
- `--field-value <id=value>` - Canonical field-id=value pair from the connection's `fields` list (repeatable); preferred over the legacy `--field`
- `--choice-id <id>` - Canonical choice ID from the connection's `choices` list
Expand Down
96 changes: 87 additions & 9 deletions cmd/api_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ type APIKeysCreateInput struct {
}

type APIKeysListInput struct {
Limit int
Offset int
Output string
Limit int
Offset int
Name string
Query string
Status string
IncludeDeleted bool
SortBy string
SortDirection string
Output string
}

type APIKeysGetInput struct {
Expand Down Expand Up @@ -116,6 +122,50 @@ func (c APIKeysCmd) List(ctx context.Context, in APIKeysListInput) error {
if in.Offset > 0 {
params.Offset = kernel.Int(int64(in.Offset))
}
if in.Name != "" {
params.Name = kernel.String(in.Name)
}
if in.Query != "" {
params.Query = kernel.String(in.Query)
}
// Prefer the newer --status filter; fall back to the deprecated
// --include-deleted so existing scripts keep working.
if in.Status != "" {
switch in.Status {
case "active":
params.Status = kernel.APIKeyListParamsStatusActive
case "deleted":
params.Status = kernel.APIKeyListParamsStatusDeleted
case "all":
params.Status = kernel.APIKeyListParamsStatusAll
default:
return fmt.Errorf("invalid --status value: %s (must be 'active', 'deleted', or 'all')", in.Status)
}
} else if in.IncludeDeleted {
params.IncludeDeleted = kernel.Opt(true)
}
if in.SortBy != "" {
switch in.SortBy {
case "created_at":
params.SortBy = kernel.APIKeyListParamsSortByCreatedAt
case "name":
params.SortBy = kernel.APIKeyListParamsSortByName
case "expires_at":
params.SortBy = kernel.APIKeyListParamsSortByExpiresAt
default:
return fmt.Errorf("invalid --sort-by value: %s (must be 'created_at', 'name', or 'expires_at')", in.SortBy)
}
}
if in.SortDirection != "" {
switch in.SortDirection {
case "asc":
params.SortDirection = kernel.APIKeyListParamsSortDirectionAsc
case "desc":
params.SortDirection = kernel.APIKeyListParamsSortDirectionDesc
default:
return fmt.Errorf("invalid --sort-direction value: %s (must be 'asc' or 'desc')", in.SortDirection)
}
}

page, err := c.apiKeys.List(ctx, params)
if err != nil {
Expand All @@ -136,17 +186,27 @@ func (c APIKeysCmd) List(ctx context.Context, in APIKeysListInput) error {
return nil
}

table := pterm.TableData{{"ID", "Name", "Scope", "Project", "Masked Key", "Expires At", "Created At"}}
// Only surface Deleted At when the filter can actually return deleted keys.
showDeletedAt := in.IncludeDeleted || in.Status == "deleted" || in.Status == "all"
header := []string{"ID", "Name", "Scope", "Project", "Masked Key", "Expires At", "Created At"}
if showDeletedAt {
header = append(header, "Deleted At")
}
table := pterm.TableData{header}
for _, key := range keys {
table = append(table, []string{
row := []string{
key.ID,
key.Name,
formatAPIKeyScope(key),
formatAPIKeyProject(key),
key.MaskedKey,
formatAPIKeyExpiresAt(key),
util.FormatLocal(key.CreatedAt),
})
}
if showDeletedAt {
row = append(row, util.FormatLocal(key.DeletedAt))
}
table = append(table, row)
}
PrintTableNoPad(table, true)
return nil
Expand Down Expand Up @@ -362,11 +422,23 @@ func runAPIKeysList(cmd *cobra.Command, args []string) error {
c := getAPIKeysHandler(cmd)
limit, _ := cmd.Flags().GetInt("limit")
offset, _ := cmd.Flags().GetInt("offset")
name, _ := cmd.Flags().GetString("name")
query, _ := cmd.Flags().GetString("query")
status, _ := cmd.Flags().GetString("status")
includeDeleted, _ := cmd.Flags().GetBool("include-deleted")
sortBy, _ := cmd.Flags().GetString("sort-by")
sortDirection, _ := cmd.Flags().GetString("sort-direction")
output, _ := cmd.Flags().GetString("output")
return c.List(cmd.Context(), APIKeysListInput{
Limit: limit,
Offset: offset,
Output: output,
Limit: limit,
Offset: offset,
Name: name,
Query: query,
Status: status,
IncludeDeleted: includeDeleted,
SortBy: sortBy,
SortDirection: sortDirection,
Output: output,
})
}

Expand Down Expand Up @@ -468,6 +540,12 @@ func init() {
addJSONOutputFlag(apiKeysListCmd)
apiKeysListCmd.Flags().Int("limit", 0, "Maximum number of results to return")
apiKeysListCmd.Flags().Int("offset", 0, "Number of results to skip")
apiKeysListCmd.Flags().String("name", "", "Exact-match filter on API key name (names are not unique, so several keys may match)")
apiKeysListCmd.Flags().String("query", "", "Search API keys by name, creator, or project (identifiers and masked keys match by exact value or prefix)")
apiKeysListCmd.Flags().String("status", "", "Filter by status: 'active' (default), 'deleted', or 'all'")
apiKeysListCmd.Flags().Bool("include-deleted", false, "Deprecated: Use --status all instead. Include soft-deleted API keys in the results")
apiKeysListCmd.Flags().String("sort-by", "", "Sort by: created_at, name, or expires_at")
apiKeysListCmd.Flags().String("sort-direction", "", "Sort direction: asc or desc")

addJSONOutputFlag(apiKeysGetCmd)
apiKeysGetCmd.Flags().Bool("include-deleted", false, "Include soft-deleted API keys in the lookup")
Expand Down
28 changes: 20 additions & 8 deletions cmd/audit_logs_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type AuditLogsDownloadInput struct {
Service string
AuthStrategy string
UserIDs []string
Format string
To string
Force bool
}
Expand All @@ -50,7 +51,7 @@ func (c AuditLogsCmd) Download(ctx context.Context, in AuditLogsDownloadInput) e

outPath := in.To
if outPath == "" {
outPath = defaultAuditLogsDownloadPath(params.Start, params.End)
outPath = defaultAuditLogsDownloadPath(params.Start, params.End, params.Format)
}
partialPath := outPath + ".partial"
out, err := openAuditLogsDownloadOutput(partialPath, outPath, in.Force)
Expand Down Expand Up @@ -202,7 +203,14 @@ func buildAuditLogsDownloadParams(in AuditLogsDownloadInput) (kernel.AuditLogExp

params.Start = start
params.End = end
params.Format = kernel.AuditLogExportChunkParamsFormatJSONLGz
switch in.Format {
case "", "jsonl.gz":
params.Format = kernel.AuditLogExportChunkParamsFormatJSONLGz
case "jsonl":
params.Format = kernel.AuditLogExportChunkParamsFormatJSONL
default:
return params, fmt.Errorf("invalid --format value: %s (must be 'jsonl.gz' or 'jsonl')", in.Format)
}
if in.Search != "" {
params.Search = kernel.String(in.Search)
}
Expand All @@ -222,9 +230,9 @@ func buildAuditLogsDownloadParams(in AuditLogsDownloadInput) (kernel.AuditLogExp
return params, nil
}

func defaultAuditLogsDownloadPath(start, end time.Time) string {
func defaultAuditLogsDownloadPath(start, end time.Time, format kernel.AuditLogExportChunkParamsFormat) string {
const stamp = "20060102"
return fmt.Sprintf("audit-logs-%s-%s.jsonl.gz", start.UTC().Format(stamp), end.UTC().Format(stamp))
return fmt.Sprintf("audit-logs-%s-%s.%s", start.UTC().Format(stamp), end.UTC().Format(stamp), format)
}

// checkAuditLogsDownloadTarget rejects paths that are not replaceable
Expand Down Expand Up @@ -289,20 +297,23 @@ func runAuditLogsDownload(cmd *cobra.Command, args []string) error {
service, _ := cmd.Flags().GetString("service")
authStrategy, _ := cmd.Flags().GetString("auth-strategy")
userIDs, _ := cmd.Flags().GetStringArray("user-id")
format, _ := cmd.Flags().GetString("format")
to, _ := cmd.Flags().GetString("to")
force, _ := cmd.Flags().GetBool("force")

return c.Download(cmd.Context(), AuditLogsDownloadInput{
Start: start, End: end, Search: search, Method: method,
ExcludeMethod: excludeMethod, IncludeGet: includeGet, Service: service,
AuthStrategy: authStrategy, UserIDs: userIDs, To: to, Force: force,
AuthStrategy: authStrategy, UserIDs: userIDs, Format: format,
To: to, Force: force,
})
}

var auditLogsDownloadCmd = &cobra.Command{
Use: "download",
Short: "Download audit logs as gzip-compressed JSONL",
Long: "Download audit logs as gzip-compressed JSONL in verified chunks. The time range is [start, end).\n\n" +
Short: "Download audit logs as JSONL",
Long: "Download audit logs as JSONL in verified chunks. The time range is [start, end).\n\n" +
"Output is gzip-compressed by default; pass --format jsonl for uncompressed JSONL.\n\n" +
"The API allows at most 30 days per download.\n\n" +
"GET requests are excluded by default; pass --include-get to include them.\n\n" +
"The output file is published only after every chunk is downloaded.",
Expand All @@ -321,7 +332,8 @@ func init() {
auditLogsDownloadCmd.Flags().String("service", "", "Filter by service")
auditLogsDownloadCmd.Flags().String("auth-strategy", "", "Filter by authentication strategy")
auditLogsDownloadCmd.Flags().StringArray("user-id", nil, "Filter by user ID (repeatable)")
auditLogsDownloadCmd.Flags().String("to", "", "Output .jsonl.gz file path")
auditLogsDownloadCmd.Flags().String("format", "jsonl.gz", "Export format: jsonl.gz or jsonl")
auditLogsDownloadCmd.Flags().String("to", "", "Output file path (defaults to a name derived from the window and --format)")
auditLogsDownloadCmd.Flags().Bool("force", false, "Overwrite the output file")
_ = auditLogsDownloadCmd.MarkFlagRequired("start")
_ = auditLogsDownloadCmd.MarkFlagRequired("end")
Expand Down
31 changes: 29 additions & 2 deletions cmd/audit_logs_download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,36 @@ func TestAuditLogsDownloadDoesNotRetryClientErrors(t *testing.T) {
func TestDefaultAuditLogsDownloadPath(t *testing.T) {
start := time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2026, 6, 28, 0, 0, 0, 0, time.UTC)
path := defaultAuditLogsDownloadPath(start, end)

assert.Equal(t, "audit-logs-20260601-20260628.jsonl.gz", path)
gz := defaultAuditLogsDownloadPath(start, end, kernel.AuditLogExportChunkParamsFormatJSONLGz)
assert.Equal(t, "audit-logs-20260601-20260628.jsonl.gz", gz)

plain := defaultAuditLogsDownloadPath(start, end, kernel.AuditLogExportChunkParamsFormatJSONL)
assert.Equal(t, "audit-logs-20260601-20260628.jsonl", plain)
}

func TestAuditLogsDownloadFormatParam(t *testing.T) {
base := AuditLogsDownloadInput{Start: "2026-06-01", End: "2026-06-02"}

for _, tc := range []struct {
format string
want kernel.AuditLogExportChunkParamsFormat
}{
{format: "", want: kernel.AuditLogExportChunkParamsFormatJSONLGz},
{format: "jsonl.gz", want: kernel.AuditLogExportChunkParamsFormatJSONLGz},
{format: "jsonl", want: kernel.AuditLogExportChunkParamsFormatJSONL},
} {
in := base
in.Format = tc.format
params, err := buildAuditLogsDownloadParams(in)
require.NoError(t, err)
assert.Equal(t, tc.want, params.Format)
}

in := base
in.Format = "csv"
_, err := buildAuditLogsDownloadParams(in)
require.ErrorContains(t, err, "invalid --format value")
}

func TestAuditLogsDownloadRejectsBadChunkBeforeWriting(t *testing.T) {
Expand Down
Loading
Loading