Kernel Hardening Guide
Cloud browser infrastructure security hardening for API key scoping, project isolation, managed-auth credential protection, Chrome policy enforcement, egress control, and audit log streaming
Product Editions Covered: Developer, Hobbyist, Start-Up, Enterprise
Overview
Kernel (kernel.sh) provides cloud browser infrastructure for AI agents and web automations: isolated Chromium instances that spin up in milliseconds, controlled via REST API, SDKs (TypeScript, Python, Go), a first-party CLI, and a hosted MCP server. Agents drive these browsers with computer-use actions, Playwright execution, or CDP, and the platform persists authenticated session state (cookies, local storage), stored login credentials, replays, and telemetry on the organization’s behalf.
That concentration is exactly why hardening matters. A Kernel organization accumulates three classes of high-value material: long-lived API keys that can create and control browsers, stored credentials and browser profiles that hold live authenticated sessions for third-party services, and agent egress — outbound traffic to arbitrary websites executed on your behalf. An attacker who obtains an over-scoped API key doesn’t just get compute: they get your agents’ logins, their session cookies, and a residential-grade browser fleet to abuse them from. Because agents act on untrusted web content, prompt injection against the agent layer (OWASP GenAI LLM Top 10 2026, LLM01) can also weaponize a browser that hasn’t been execution-constrained.
This guide hardens the Kernel platform surface itself: key scoping, project isolation, credential and profile protection, browser execution policy, egress control, and audit visibility.
Intended Audience
- Security engineers responsible for AI agent infrastructure
- Platform/DevOps teams operating browser automation fleets
- GRC professionals assessing agentic tooling
- Third-party risk managers reviewing Kernel as a subprocessor
How to Use This Guide
- L1 (Baseline): Essential controls for all organizations
- L2 (Hardened): Enhanced controls for security-sensitive environments
- L3 (Maximum Security): Strictest controls for regulated industries (healthcare, finance, government)
Scope
Covers tenant-level hardening of the Kernel platform: organization, projects, API keys, managed auth, profiles, browser configuration, proxies, extensions, telemetry, and audit logs. Does not cover application-layer security of the agents you build on top of Kernel (prompt design, tool permissioning inside your agent framework), or the security of target websites your agents visit.
A note on benchmarks: no CIS Benchmark, DISA STIG, or CISA SCuBA baseline exists for Kernel as of this writing. Controls in this guide originate from Kernel’s first-party configuration documentation (Tier 1), with framework mappings to NIST 800-53, SOC 2, and ISO 27001:2022 at the control-family level — flagged here as no benchmark equivalent yet.
Table of Contents
- API Keys & Access Scoping
- Project Isolation & Resource Governance
- Credential & Session-State Protection
- Browser Execution Hardening
- Monitoring & Audit
- Third-Party Integration Security
- Compliance Quick Reference
1. API Keys & Access Scoping
1.1 Scope API Keys to Projects
Profile Level: L1 (Baseline) NIST 800-53: AC-6, AC-3 CIS Controls v8: 6 (Access Control Management)
Description
Kernel API keys come in two scopes: org-scoped keys that can access resources across the entire organization, and project-scoped keys that can only access resources inside the project they were issued for. Issue project-scoped keys for every workload, and reserve org-scoped keys for the small set of administrative operations that genuinely need them.
Rationale
Why This Matters:
- A leaked org-scoped key exposes every project’s browsers, profiles, stored credentials, proxies, and deployments; a project-scoped key caps the blast radius to one environment
- Kernel enforces scoping server-side: a project-scoped key that sends a conflicting
X-Kernel-Project-Idheader receives403 Forbidden, and can only mint further keys for its own project - CI pipelines, per-customer workloads, and experiments each get least-privilege credentials instead of sharing one master key
Attack Prevented: Lateral movement across environments after a single key compromise (e.g., a staging key leaked in CI logs being used to read production browser profiles).
Prerequisites
- An existing org-scoped key (or dashboard access) to mint new keys
- Projects created for each environment (see Control 2.1)
ClickOps Implementation
Kernel’s documentation implements key management through the API, CLI, and SDKs; no dashboard console path for key creation is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# Create a PROJECT-SCOPED key: it can only access resources inside the
# project it was issued for. The plaintext key is returned exactly once —
# store it in your secret manager immediately.
kernel api-keys create \
--name staging-ci \
--days-to-expire 30 \
--project-id proj_staging_9f3k \
--output json
# Omitting --project-id creates an ORG-SCOPED key that can reach every
# project. Reserve org-scoped keys for administrative operations only.
kernel api-keys create \
--name org-admin-breakglass \
--days-to-expire 90 \
--output json
# Verify the effective scope of the key you are holding: GET /auth/context
# returns the principal, organization, and the credential's maximum and
# effective scope — without exposing the secret.
curl -sS https://api.onkernel.com/auth/context \
-H "Authorization: Bearer ${KERNEL_API_KEY}"
Validation & Testing
- Call
GET /auth/contextwith the new key — the response returns the authenticated principal, organization, and the credential’s maximum and effective scope, without exposing the secret - Attempt to access a resource in a different project using the project-scoped key
Expected result: The auth context shows the project binding; cross-project access fails with 403 Forbidden.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.3 | Role-based access and least privilege |
| NIST 800-53 | AC-6 | Least privilege |
| ISO 27001:2022 | 5.15 | Access control |
1.2 Set Expiration on Every API Key
Profile Level: L1 (Baseline) NIST 800-53: IA-5 CIS Controls v8: 5 (Account Management)
Description
Kernel API keys accept a days_to_expire parameter (1–3650 days) at creation. Set an explicit, short expiration on every key so forgotten credentials age out instead of accumulating as permanent attack surface. Kernel returns the plaintext key exactly once at creation; listing and retrieval return only a masked_key.
Rationale
Why This Matters:
- Non-expiring machine credentials are a top infostealer target — a key harvested from a laptop or CI log today should not still work next year
- Forced expiry creates a natural rotation cadence and an inventory signal: a key that expires unnoticed was a key nobody needed
- Kernel’s own guidance: treat the key like a password — keep it out of client-side code, store it in a secret manager, rotate it when access changes
Attack Prevented: Long-tail abuse of stale credentials harvested months earlier (credential-stuffing supply chains documented in Push Security’s Browser & Identity Attacks Matrix).
Prerequisites
- A secret manager to hold the plaintext key (returned only once)
ClickOps Implementation
No dashboard console path for key creation is documented. Use the Code implementation below.
Code Implementation
Code Pack: API Script
# days_to_expire: 1-3650; null means never — do not ship null.
# The plaintext key appears once in this response and never again;
# subsequent list/get calls return only masked_key.
curl -sS -X POST https://api.onkernel.com/org/api_keys \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "reporting-agent",
"days_to_expire": 30,
"project_id": "proj_production_a1b2"
}'
# Inventory check: list keys (masked) and flag any without an expiry.
curl -sS "https://api.onkernel.com/org/api_keys" \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}" |
python3 -c "
import json, sys
keys = json.load(sys.stdin)
items = keys if isinstance(keys, list) else keys.get('items', keys.get('data', []))
for k in items:
if not k.get('expires_at'):
print('NO EXPIRY:', k.get('id'), k.get('name'))
"
Validation & Testing
- List keys via
GET /org/api_keysand confirm every key shows an expiration - Confirm the response contains
masked_keyvalues only — no plaintext
Expected result: Zero keys without expiry; no plaintext key material in list responses.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Logical access security — credential lifecycle |
| NIST 800-53 | IA-5 | Authenticator management |
| ISO 27001:2022 | 5.17 | Authentication information |
1.3 Rotate API Keys with Bounded Grace Periods
Profile Level: L1 (Baseline) NIST 800-53: IA-5 CIS Controls v8: 5 (Account Management)
Description
Kernel’s rotate operation (POST /org/api_keys/{id}/rotate) issues a replacement key that copies the rotated key’s name and project binding, and schedules the old key to expire after a grace period so in-flight callers can swap over. The grace period defaults to 7 days and is configurable via expire_in_days (0–3650); expire_in_days: 0 revokes the old key immediately. Rotate on a schedule, on personnel change, and immediately on suspected exposure.
Rationale
Why This Matters:
- Rotation with a grace period makes routine key hygiene a zero-downtime operation — there is no availability excuse for year-old keys
- Immediate revocation (
expire_in_days: 0) is the documented kill switch for a confirmed leak - The new plaintext key is returned exactly once, forcing secret-manager discipline on every rotation
Attack Prevented: Continued use of a compromised credential after exposure; the grace-period bound also limits how long a superseded key remains a live target.
Prerequisites
- Inventory of where each key is deployed (CI secrets, agent runtimes) so consumers can be updated within the grace window
ClickOps Implementation
No dashboard console path for key rotation is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# Routine rotation: issues a new key (same name and project binding),
# old key stays valid for the grace period so in-flight callers can swap.
# --days-to-expire: lifetime of the NEW key
# --expire-in-days: grace period for the OLD key (default 7)
kernel api-keys rotate key_01jwv4tn5m8k3q2v7x9p0a1bc2 \
--days-to-expire 90 \
--expire-in-days 7 \
--yes --output json
# Confirmed leak: rotate with an immediate kill of the old key.
kernel api-keys rotate key_01jwv4tn5m8k3q2v7x9p0a1bc2 \
--expire-in-days 0 \
--yes --output json
# Or delete outright. A key cannot delete itself — authenticate
# with a DIFFERENT (admin) key for the deletion.
kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes
Validation & Testing
- Rotate a test key with
--expire-in-days 7, then verify the old key still authenticates during the grace window and the new key carries the same project binding viaGET /auth/context - Rotate with
--expire-in-days 0and confirm the old key is rejected immediately
Expected result: Old key dies exactly at the configured boundary; new key inherits scope.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Credential rotation and revocation |
| NIST 800-53 | IA-5 | Authenticator management |
| ISO 27001:2022 | 5.17 | Authentication information |
1.4 Audit and Remove Unused API Keys
Profile Level: L2 (Hardened) NIST 800-53: AC-2 CIS Controls v8: 5 (Account Management)
Description
Review the organization’s key inventory (GET /org/api_keys, masked) on a cadence and delete keys that no longer map to a live workload. Kernel enforces a safety property worth building into your runbook: an API key cannot delete itself — deletion must be authorized by a different key, so a revocation request can never be signed by the credential it is revoking.
Rationale
Why This Matters:
- Every dormant key is standing attack surface with no owner watching it
- The cannot-delete-itself rule means your break-glass admin key and your day-to-day keys must be distinct — plan that separation before the incident
- Masked listing makes inventory review safe to automate and archive
Attack Prevented: Abuse of orphaned credentials from departed staff, finished projects, or abandoned experiments.
Prerequisites
- A second (admin) key to authorize deletions
- Key-to-workload ownership records (names like
staging-cihelp — see Control 1.1)
ClickOps Implementation
No dashboard console path for key inventory is documented. Use the Code implementation below.
Code Implementation
Code Pack: API Script
# List all keys for the organization. Responses are MASKED (masked_key),
# so this inventory is safe to archive with review records.
curl -sS "https://api.onkernel.com/org/api_keys" \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}" \
-o api-key-inventory-$(date +%F).json
# Reconcile every key against a named owner/workload before deleting.
python3 -c "
import json
for k in json.load(open('api-key-inventory-$(date +%F).json')) or []:
print(k.get('id'), '|', k.get('name'), '|', k.get('masked_key'), '|', k.get('expires_at'))
"
# Delete a key that no longer maps to a live workload.
# A key cannot delete itself: KERNEL_ADMIN_KEY must be a different key
# than the one being removed, so revocation can never be self-signed.
curl -sS -X DELETE "https://api.onkernel.com/org/api_keys/key_01jwv4tn5m8k3q2v7x9p0a1bc2" \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}"
Validation & Testing
- Run the inventory script and reconcile every key against a named owner/workload
- Attempt to delete a key using itself as the authorizing credential
Expected result: Unowned keys are deleted; self-deletion is rejected by the API.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.2 | Removal of access when no longer required |
| NIST 800-53 | AC-2 | Account management |
| ISO 27001:2022 | 5.18 | Access rights |
1.5 Prefer OAuth Over Static Keys for MCP Access
Profile Level: L2 (Hardened) NIST 800-53: IA-2 CIS Controls v8: 6 (Access Control Management)
Description
Kernel’s hosted MCP server (mcp.onkernel.com) gives MCP-capable clients (Claude, Cursor, VS Code, Zed, and others) direct control of browsers, profiles, proxies, and shell execution inside browser VMs. It supports two authentication modes: OAuth 2.1 (recommended, browser-based user authorization) and static API keys passed as a Bearer header. Use OAuth for human-driven clients; reserve API-key auth for headless automation that cannot complete an OAuth flow — and scope those keys per Control 1.1.
Rationale
Why This Matters:
- MCP config files are synced, committed, and screen-shared constantly; a static key embedded in MCP settings is one paste away from exposure, while an OAuth grant is user-bound and revocable
- The MCP server exposes high-consequence tools (
exec_commandruns shell commands inside browser VMs) — the authentication that gates them deserves the stronger mode - Kernel’s documentation is explicit: API keys bypass the OAuth flow; keep them out of client-side code
Attack Prevented: Credential theft from committed or synced MCP client configuration files.
Prerequisites
- An MCP client that supports OAuth 2.1 authorization flows
ClickOps Implementation
Step 1: Connect via OAuth (recommended)
- Add the Kernel MCP server (
https://mcp.onkernel.com/mcp) in your MCP client’s connector settings - Complete the browser-based OAuth authorization prompt when the client initiates the connection
Step 2: Restrict API-key mode to headless automation
- Where a headless client must use a key, issue a dedicated project-scoped key (Control 1.1) with a short expiry (Control 1.2)
- Store the config outside version control
Time to Complete: ~10 minutes
Code Implementation
Code Pack: Config
// RECOMMENDED — OAuth 2.1: omit auth headers entirely. The MCP client
// launches a browser-based authorization prompt on first connection;
// the grant is user-bound and revocable, and no secret lives in this file.
{
"mcpServers": {
"kernel": {
"url": "https://mcp.onkernel.com/mcp"
}
}
}
// HEADLESS ONLY — static API key. The server treats non-JWT bearer tokens
// as API keys. If you must use this mode: issue a project-scoped, expiring
// key (Controls 1.1-1.2), inject it from the environment at render time,
// and keep this file out of version control.
{
"mcpServers": {
"kernel": {
"url": "https://mcp.onkernel.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_KERNEL_API_KEY"
}
}
}
}
Validation & Testing
- Review all MCP client configurations in your fleet for hardcoded
Authorization: Bearervalues - Confirm headless configs reference environment-injected, project-scoped keys
Expected result: Human clients authenticate via OAuth; any static keys are project-scoped, expiring, and absent from version control.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Authentication of users and systems |
| NIST 800-53 | IA-2 | Identification and authentication |
| ISO 27001:2022 | 5.16 | Identity management |
2. Project Isolation & Resource Governance
2.1 Isolate Environments with Projects
Profile Level: L1 (Baseline) NIST 800-53: SC-7, AC-4 CIS Controls v8: 12 (Network Infrastructure Management)
Description
Projects are Kernel’s isolation boundary: each project holds its own browsers, profiles, credentials, proxies, extensions, deployments, and browser pools, and API keys can be bound to a single project. Create separate projects for production, staging, and experiments so an agent (or a key) in one environment cannot touch another’s session state. Every organization has a Default project that cannot be deleted while it is the last active project; requests without a project binding land there.
Rationale
Why This Matters:
- Browser profiles and stored credentials are live authentication material — a staging experiment must not be able to load production session cookies
- Project-scoped keys plus per-project resources turn a key leak into a single-environment incident instead of an organization-wide one
- Resources created before projects existed were migrated into Default; until you segment them, everything shares one blast radius
Attack Prevented: Cross-environment contamination — experimental or compromised agent code reaching production profiles, credentials, and proxies.
Prerequisites
- Org-scoped key or dashboard access to create projects
- A naming convention (
production,staging, per-customer, per-team)
ClickOps Implementation
Step 1: Create projects
- In the Kernel dashboard, navigate to the Projects section
- Create one project per environment (e.g.,
production,staging)
Step 2: Re-home existing resources
- Inventory resources sitting in the Default project
- Recreate long-lived resources (profiles, proxies, pools) inside the correct project, then retire the Default-project copies
Time to Complete: ~20 minutes plus resource migration
Code Implementation
Code Pack: Terraform
terraform {
required_providers {
kernel = {
source = "kernel/kernel"
}
}
}
# Authentication comes from the environment where Terraform runs:
# KERNEL_API_KEY — a dedicated, expiring CI key (Controls 1.1-1.3)
# KERNEL_PROJECT_ID — optional default project binding
# Never place the key in provider config or committed tfvars.
provider "kernel" {}
# One project per environment: browsers, profiles, credentials, proxies,
# extensions, deployments, and pools are all project-scoped, so a key or
# workload in staging can never touch production session state.
resource "kernel_project" "production" {
name = "production"
}
resource "kernel_project" "staging" {
name = "staging"
}
Validation & Testing
kernel projects list(orGET /org/projects) — confirm the expected project set- With a
production-scoped key, attempt to liststagingprofiles
Expected result: Environments enumerate separately; cross-project access with a scoped key returns 403 Forbidden.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Segregation of environments |
| NIST 800-53 | SC-7 | Boundary protection |
| ISO 27001:2022 | 8.31 | Separation of development, test and production environments |
2.2 Cap Browser Concurrency at Org and Project Level
Profile Level: L2 (Hardened) NIST 800-53: SC-6 CIS Controls v8: 4 (Secure Configuration of Enterprise Assets and Software)
Description
Kernel enforces a resolution hierarchy for how many browsers can run at once: a project’s explicit override wins, else the organization’s default per-project cap, else the organization-wide limit. Set an org default (PATCH /org/limits, field default_project_max_concurrent_sessions) and explicit per-project overrides (PATCH /org/projects/{id}/limits, field max_concurrent_sessions) so a runaway or hijacked workload cannot consume the entire fleet. Limits cover both on-demand sessions and browser-pool reservations.
Rationale
Why This Matters:
- An agent caught in a loop — or an attacker with a stolen key — can otherwise spin browsers to the organization ceiling, running up cost and starving production
- Per-project caps convert a single noisy tenant into a contained event
- Setting a value to
0removes that cap; treat0as a deliberate, reviewed exception, not a default
Attack Prevented: Resource-exhaustion abuse of a compromised key (cryptomining-style fleet abuse, cost-inflation attacks, denial of service to sibling projects).
Prerequisites
- Org-scoped API key (project-limit updates are org-level administration)
- Baseline of legitimate peak concurrency per project
ClickOps Implementation
No dashboard console path for limits is documented. Use the Code implementation below (kernel org CLI commands are also available).
Code Implementation
Code Pack: API Script
# Set the org-wide DEFAULT per-project concurrency cap. Applies to every
# project without an explicit override; cannot exceed the org's own limit.
# Setting the value to 0 REMOVES the default — treat 0 as a reviewed exception.
curl -sS -X PATCH https://api.onkernel.com/org/limits \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{ "default_project_max_concurrent_sessions": 10 }'
# Read back the effective org posture:
# max_concurrent_sessions — org ceiling (on-demand + pool reservations)
# default_project_max_concurrent_sessions — the default cap you just set
curl -sS https://api.onkernel.com/org/limits \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}"
# Explicit override for a hot project. Omitted fields are left unchanged;
# a field set to 0 removes that project-level cap (org default applies).
curl -sS -X PATCH "https://api.onkernel.com/org/projects/proj_production_a1b2/limits" \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{ "max_concurrent_sessions": 25 }'
# Verify: null values mean no project-level cap (org limit applies).
curl -sS "https://api.onkernel.com/org/projects/proj_production_a1b2/limits" \
-H "Authorization: Bearer ${KERNEL_ADMIN_KEY}"
Validation & Testing
GET /org/limits— confirmdefault_project_max_concurrent_sessionsis set andmax_concurrent_sessionsreflects the org ceilingGET /org/projects/{id}/limitsfor each production project — confirm explicit overrides (null means the org default applies)
Expected result: No project can exceed its reviewed concurrency budget.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 / A1.1 | Capacity management and availability |
| NIST 800-53 | SC-6 | Resource availability |
| ISO 27001:2022 | 8.6 | Capacity management |
3. Credential & Session-State Protection
3.1 Inject App Secrets at Deploy Time, Never in Code
Profile Level: L1 (Baseline) NIST 800-53: IA-5, SA-3 CIS Controls v8: 16 (Application Software Security)
Description
Kernel apps receive secrets as environment variables injected at deployment: kernel deploy my_app.ts --env KEY=value for individual values, or --env-file .env to load a local env file. Code reads them via process.env / os.environ at runtime. Keep secrets out of app source and out of the repository; for per-invocation secrets passed through the invocation payload, Kernel’s documentation directs you to encrypt them in your app rather than sending plaintext.
Rationale
Why This Matters:
- Deployed app code is long-lived artifact material; environment injection keeps credentials out of it
- A
.envfile that never enters version control is auditable at one choke point — the deploy command - Payload-borne secrets traverse invocation records; the documented pattern is client-side encryption before the payload, decryption inside the app
Attack Prevented: Credential harvesting from committed source, shared app bundles, or invocation payload logs.
Prerequisites
- Kernel CLI authenticated (
kernel auth) .envfiles excluded from version control
ClickOps Implementation
No dashboard console path for app secrets is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# Individual values (from your secret manager, via the shell environment):
kernel deploy my_app.ts --env OPENAI_API_KEY="${OPENAI_API_KEY}"
# Or load a local .env file that is excluded from version control:
kernel deploy my_app.ts --env-file .env
# App code reads injected values at runtime via the standard environment:
# TypeScript: process.env.OPENAI_API_KEY
# Python: os.environ["OPENAI_API_KEY"]
# Guard the choke point: .env must never be committed, and app source
# must not embed key material.
grep -qxF '.env' .gitignore || echo 'MISSING: add .env to .gitignore'
git grep -nE '(sk-[A-Za-z0-9]|api[_-]?key\s*=\s*["'"'"'][^"'"'"']{8,})' -- '*.ts' '*.py' \
&& echo 'FAIL: possible hardcoded secrets above' || echo 'OK: no hardcoded secrets found'
Validation & Testing
- Grep the app source and repository history for hardcoded key material
- Redeploy with a rotated value and confirm the app picks it up from
process.envwithout a code change
Expected result: Secrets exist only in the deploy-time environment and your secret manager.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Protection of credentials |
| NIST 800-53 | IA-5(7) | No embedded unencrypted static authenticators |
| ISO 27001:2022 | 8.28 | Secure coding |
3.2 Harden Stored Credentials for Managed Auth
Profile Level: L2 (Hardened) NIST 800-53: IA-5, SC-28 CIS Controls v8: 3 (Data Protection)
Description
Kernel Managed Auth can store login credentials so agents re-authenticate automatically when sessions expire. Stored values are encrypted with per-organization keys and are write-only — they cannot be retrieved via API after creation; list responses never include values; one-time codes (TOTP codes, SMS) are never saved, though a totp_secret can be stored for TOTP generation. Harden the lifecycle: store credentials deliberately (kernel.credentials.create() with name, domain, values), disable automatic capture where you don’t want it (save_credentials: false on login flows), keep one credential per account, and delete credentials on decommission — deletion unlinks them from associated connections so those connections can no longer auto-authenticate.
Rationale
Why This Matters:
- These are real third-party passwords for the accounts your agents operate; write-only storage means even a read-scoped compromise cannot exfiltrate values via the API
- Automatic capture during Hosted UI logins is convenient but silent — an org that doesn’t decide
save_credentialsper flow accumulates credentials it never inventoried - Kernel’s protections (never logged, never shared with LLMs, isolated login execution) cover the platform side; account-per-credential and prompt deletion are your side
Attack Prevented: Bulk credential exfiltration via API access, and orphaned auto-auth paths for accounts that should have been offboarded.
Prerequisites
- Inventory of accounts your agents authenticate to
- Managed Auth in use (Hosted UI, React component, or programmatic flow)
ClickOps Implementation
Step 1: Decide capture policy per flow
- For each Hosted UI / programmatic login flow, set
save_credentials: falseunless automatic re-auth for that account is an accepted, documented need
Step 2: Offboard with deletion
- When an agent account is retired, delete its stored credential and verify the associated connection no longer auto-authenticates
Time to Complete: ~15 minutes plus inventory
Code Implementation
Code Pack: SDK Script
// Pre-store a credential for headless automation. Values are encrypted with
// per-organization keys and are WRITE-ONLY: they cannot be read back via the
// API after creation. One-time codes (SMS/TOTP codes) are never saved;
// totp_secret enables automatic TOTP generation for 2FA logins.
// Keep ONE credential per account — never share a credential across accounts.
await kernel.credentials.create({
name: 'crm-agent-login', // unique within the project
domain: 'crm.example.com', // target domain this credential is for
values: {
username: 'agent-svc@example.com',
password: process.env.CRM_AGENT_PASSWORD, // from your secret manager
},
totp_secret: process.env.CRM_AGENT_TOTP_SECRET, // base32, optional
});
// Hosted UI / programmatic login flows SAVE credentials by default.
// Where automatic capture is not an accepted, documented need for the
// account, opt out on the flow configuration:
const flow = {
save_credentials: false,
};
// Decommission: deleting a credential unlinks it from associated
// connections, so they can no longer auto-authenticate.
await fetch('https://api.onkernel.com/credentials/crm-agent-login', {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.KERNEL_API_KEY}` },
});
Validation & Testing
- Attempt to read back a stored credential’s values via the API
- After deleting a test credential, trigger its connection’s re-auth and confirm it fails
Expected result: Values are unreadable post-creation; deleted credentials cannot auto-authenticate.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Protection of authentication credentials |
| NIST 800-53 | SC-28 | Protection of information at rest |
| ISO 27001:2022 | 5.17 | Authentication information |
3.3 Back Managed Auth with 1Password Vaults
Profile Level: L2 (Hardened) NIST 800-53: IA-5 CIS Controls v8: 3 (Data Protection)
Description
Instead of storing raw credentials in Kernel, connect a 1Password service account: Managed Auth then retrieves credentials from your vaults at authentication time, matched by the item’s website/URL field (exact domain, full URL, or wildcard). Values are never persisted in Kernel — the service-account token itself is encrypted with per-organization keys, access is bounded by the service account’s vault permissions, and 1Password logs every credential access on its side.
No benchmark equivalent yet.
Rationale
Why This Matters:
- Keeps the system of record for secrets in your existing, audited vault instead of a second store to govern
- Vault-scoped service accounts give you least privilege and instant revocation at the 1Password layer
- 1Password’s access logs add an independent audit trail for every agent credential retrieval
Attack Prevented: Sprawl of duplicated credential stores; undetected credential access (each retrieval is logged by 1Password).
Prerequisites
- 1Password plan supporting service accounts
- A dedicated vault holding only agent-facing credentials (grant the service account access to nothing else)
ClickOps Implementation
Step 1: Create the service account
- In 1Password, create a service account with access restricted to the agent-credentials vault
- Copy the token (starts with
ops_)
Step 2: Connect in Kernel
- In the Kernel dashboard, navigate to Integrations → Connect 1Password
- Enter a provider name, paste the service-account token, and confirm the accessible vaults shown
Step 3: Reference the provider in flows
- In credential objects, use auto-lookup (
{ provider: 'my-1p', auto: true }) or an explicit path ({ provider: 'my-1p', path: 'VaultName/ItemName' }) - Where multiple items match a domain, note that the first match wins — keep one item per domain in the agent vault
Time to Complete: ~20 minutes
Validation & Testing
- Run a login flow using the provider reference and confirm authentication succeeds
- Check 1Password’s access logs for the corresponding retrieval event
- Revoke the service account and confirm flows fail closed
Expected result: Credentials resolve from the vault, every access is logged, revocation is immediate.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Centralized credential management |
| NIST 800-53 | IA-5 | Authenticator management |
| ISO 27001:2022 | 5.17 | Authentication information |
3.4 Treat Browser Profiles as Credential Material
Profile Level: L1 (Baseline) NIST 800-53: SC-28, AC-6 CIS Controls v8: 3 (Data Protection)
Description
Kernel profiles persist browser state — cookies and local storage — across sessions, which means they hold live session tokens for every site an agent logged into. Profile data is encrypted end-to-end with a per-organization key, but governance is yours: profiles are project-scoped (Control 2.1), writable only when a browser is created with save_changes: true, replaced wholesale on save (last writer wins), loaded read-only by browser pools (saves are ignored, not rejected), and downloadable as archives via GET /profiles/{id_or_name}/download. Restrict which workloads may write profiles, which keys can download them, and delete profiles when their account is retired.
Rationale
Why This Matters:
- A downloaded profile archive is a portable bundle of authenticated sessions — functionally equivalent to stealing the cookies off a logged-in machine, the exact session-hijacking material catalogued in Push Security’s Browser & Identity Attacks Matrix
save_changesdiscipline prevents accidental persistence of state from untrusted browsing into a trusted profile- Concurrent writers silently overwrite each other (the browser that ends last wins) — treat write access as single-owner
Attack Prevented: Session hijacking via profile-archive exfiltration; trust-poisoning of shared profiles by untrusted workloads.
Prerequisites
- Project segmentation in place (Control 2.1)
- Named ownership for each profile (which agent/account it represents)
ClickOps Implementation
No dashboard console path for profile governance is documented. Use the Code implementation below.
Code Implementation
Code Pack: SDK Script
// Profiles persist cookies + local storage — live session tokens.
// Create the profile once, and give WRITE access (save_changes: true)
// to exactly one designated refresher workload. Saves replace the whole
// profile, and the browser that ends last overwrites the profile — so
// concurrent writers silently clobber each other.
await kernel.profiles.create({ name: 'crm-agent' });
// The ONE designated writer session:
const writer = await kernel.browsers.create({
profile: { name: 'crm-agent', save_changes: true },
});
// NOTE: state persists when the browser is deleted or times out;
// browser.close() does NOT save profile state.
// Every other consumer loads the profile WITHOUT save_changes: state from
// untrusted browsing is discarded, never persisted into the trusted profile.
const reader = await kernel.browsers.create({
profile: { name: 'crm-agent' },
});
// Browser pools enforce this for you: pooled browsers load their profile
// read-only — any save_changes is ignored (not rejected), so pooled
// browsers never write back to the profile.
Validation & Testing
- Create a browser against a profile without
save_changesand confirm post-session state is discarded - Acquire a pooled browser with the profile attached and confirm profile writes are ignored
- With a project-scoped key from another project, attempt
GET /profiles/{id}/download
Expected result: Only designated writers persist state; cross-project download is denied.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Protection of session tokens |
| NIST 800-53 | SC-28 | Protection of information at rest |
| ISO 27001:2022 | 8.1 | User endpoint devices (session state) |
4. Browser Execution Hardening
4.1 Enforce Chrome Enterprise Policies on Browsers and Pools
Profile Level: L2 (Hardened) NIST 800-53: CM-7, CM-6 CIS Controls v8: 4 (Secure Configuration of Enterprise Assets and Software)
Description
Kernel accepts a chrome_policy object — standard Chrome enterprise policies — on individual browser creation and on browser pools (where it applies to every browser in the pool). Use it to constrain what an agent-driven browser can do: URLAllowlist/URLBlocklist to restrict navigation (the documentation’s own security example blocks devtools://*, chrome://inspect, and view-source:*), DownloadRestrictions to limit file downloads, and startup/homepage pinning. The serialized payload is capped at 5 MiB, and Kernel-managed policy areas (extensions, proxy, CDP/automation) are rejected — those are governed by their own controls (4.2, 4.3).
No benchmark equivalent yet — policy values here derive from Kernel’s documented examples; tune allowlists to your workload.
Rationale
Why This Matters:
- Agents act on untrusted page content; a prompt-injected agent (OWASP GenAI LLM Top 10 2026, LLM01/LLM08) with an unrestricted browser can be steered to arbitrary destinations — a URL allowlist turns that into a contained failure
- Blocking DevTools surfaces closes an in-browser route to inspect cookies, storage, and network internals
- Pool-level policy makes the hardened configuration the default fleet-wide, not a per-callsite opt-in
Attack Prevented: Prompt-injection-driven navigation to attacker infrastructure; drive-by download execution; in-browser credential/session inspection.
Prerequisites
- Inventory of domains each workload legitimately needs (for allowlists)
- Browser pools for production workloads (policy attaches at pool level)
ClickOps Implementation
No dashboard console path for Chrome policies is documented. Use the Code implementation below.
Code Implementation
Code Pack: Terraform
# Pool-level policy applies to EVERY browser in the pool — the hardened
# configuration becomes the fleet default, not a per-callsite opt-in.
# chrome_policy is a JSON object of Chrome enterprise policies (max 5 MiB);
# Kernel-managed areas (extensions, proxy, CDP/automation) are rejected.
resource "kernel_browser_pool" "hardened_agents" {
name = "hardened-agents"
size = 5
stealth = true
chrome_policy = jsonencode({
# Contain prompt-injection-driven navigation: allow only the domains
# this workload legitimately needs; more specific entries win.
URLAllowlist = [
"crm.example.com",
"docs.example.com",
]
# Kernel's documented security example: block in-browser inspection
# surfaces (cookies, storage, network internals).
URLBlocklist = [
"devtools://*",
"chrome://inspect",
"view-source:*",
"*", # everything not on the allowlist
]
})
# Roll the new policy onto idle browsers when this config changes;
# warming/leased browsers are not rebuilt.
rebuild_idle_browsers_on_update = true
}
Validation & Testing
- In a policy-bearing browser, navigate to a blocked URL and to
devtools://— both must be refused - Attempt a restricted download
- Update the pool with
rebuild_idle_browsers_on_update/discard_all_idlebehavior in mind, and confirm idle browsers pick up the new policy
Expected result: Navigation and downloads outside policy are blocked in every pooled browser.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.8 | Prevention of unauthorized software/actions |
| NIST 800-53 | CM-7 | Least functionality |
| ISO 27001:2022 | 8.9 | Configuration management |
4.2 Vet, Pin, and Checksum Browser Extensions
Profile Level: L2 (Hardened) NIST 800-53: CM-11, SA-12 CIS Controls v8: 2 (Inventory and Control of Software Assets)
Description
Extensions loaded into Kernel browsers run inside your agents’ authenticated sessions. Kernel’s model is upload-based: kernel extensions upload ./my-extension --name my-extension registers an unpacked extension (project-scoped, unique names), browsers reference it by name at creation, and GET /extensions/{id_or_name}/metadata returns a SHA-256 checksum when available. The CLI can also fetch unpacked extensions from the Chrome Web Store (kernel extensions download-web-store). Vet extension source before upload, record the checksum, verify it before high-sensitivity runs, and treat runtime ad-hoc uploads to running browsers (POST /browsers/{id}/extensions — which restarts Chromium) as an exception path, not a norm.
Rationale
Why This Matters:
- A malicious or trojaned extension sees every page, cookie, and keystroke in the session — the browser-extension vector is a named technique class in Push Security’s Browser & Identity Attacks Matrix
- Upload-based loading is an opportunity: the artifact you vetted is the artifact that runs, and its checksum proves it
- Web Store downloads inherit Web Store supply-chain risk (developer-account takeovers); pin what you reviewed, not “latest”
Attack Prevented: Extension-borne session theft and content manipulation inside agent browsers.
Prerequisites
- An extension review step (manifest permissions, code provenance) before upload
- A record of approved extension names and checksums per project
ClickOps Implementation
No dashboard console path for extension management is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# 1. Fetch the exact unpacked artifact for review (Web Store source):
kernel extensions download-web-store \
"https://chromewebstore.google.com/detail/EXTENSION_ID" --to ./candidate-extension
# 2. REVIEW ./candidate-extension: manifest permissions, code provenance.
# Only after review, register the artifact you vetted:
kernel extensions upload ./candidate-extension --name approved-ext-v1
# 3. Record its checksum in your approval log. Metadata includes a
# lowercase hex SHA-256 checksum when available.
curl -sS "https://api.onkernel.com/extensions/approved-ext-v1/metadata" \
-H "Authorization: Bearer ${KERNEL_API_KEY}"
# Cadence check: enumerate extensions in the project and reconcile each
# checksum against the approval record.
curl -sS "https://api.onkernel.com/extensions" \
-H "Authorization: Bearer ${KERNEL_API_KEY}"
# Load extensions into browsers BY NAME so what runs is what you vetted:
kernel browsers create --extension approved-ext-v1
Validation & Testing
- Compare
metadata.checksumfor each uploaded extension against your approval record - Enumerate extensions per project (
GET /extensions) and reconcile against the approved list
Expected result: Every extension in every project matches an approved checksum.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC8.1 | Change management for software components |
| NIST 800-53 | CM-11 | User-installed software |
| ISO 27001:2022 | 8.19 | Installation of software on operational systems |
4.3 Route Egress Through Managed, Inspectable Proxies
Profile Level: L3 (Maximum Security) NIST 800-53: SC-7, AC-4 CIS Controls v8: 13 (Network Monitoring and Defense)
Description
Kernel browsers can be bound to proxy configurations (POST /proxies) including fully custom proxies: type: "custom" with config.host, config.port, optional username/password (write-only — responses return has_password: true, never the value), optional bypass_hosts (up to 100 hostnames that connect directly), and optional config.ca_bundle — a PEM CA bundle for a TLS-terminating (MITM) proxy, which Kernel validates and installs into the browser’s trust store. A CA-bundle proxy must be bound at browser creation; it cannot be hot-swapped onto a running browser. For regulated workloads, route agent egress through your own inspecting proxy so outbound traffic is logged and policy-enforced at a layer the agent cannot alter.
No benchmark equivalent yet.
Rationale
Why This Matters:
- Agents fetch and act on arbitrary web content; an egress choke point is your only network-layer view of what they actually touched
- An inspecting proxy catches exfiltration and command-and-control patterns that in-browser policy cannot see
- Write-only proxy credentials and creation-time CA binding keep the inspection layer itself tamper-resistant from the session side
Attack Prevented: Undetected data exfiltration by a hijacked agent; policy bypass via direct egress (bypass_hosts kept minimal and reviewed).
Prerequisites
- An inspecting proxy you operate (with its CA bundle in PEM form)
- Legal/privacy review for TLS inspection of agent traffic
ClickOps Implementation
No dashboard console path for proxy configuration is documented. Use the Code implementation below.
Code Implementation
Code Pack: API Script
# Register your inspecting proxy. Credentials are WRITE-ONLY: responses
# report has_password: true but never return the value. A ca_bundle
# (PEM) marks a TLS-terminating proxy — Kernel validates the bundle and
# installs it in the browser trust store. CA-bundle proxies must be bound
# at browser creation; they cannot be hot-swapped onto a running browser.
curl -sS -X POST https://api.onkernel.com/proxies \
-H "Authorization: Bearer ${KERNEL_API_KEY}" \
-H "Content-Type: application/json" \
-d @- << EOF
{
"type": "custom",
"name": "egress-inspection",
"protocol": "https",
"config": {
"host": "proxy.internal.example.com",
"port": 8443,
"username": "kernel-fleet",
"password": "${PROXY_PASSWORD}",
"ca_bundle": "$(awk 'BEGIN{ORS="\\n"} {print}' ./inspection-ca.pem)"
},
"bypass_hosts": []
}
EOF
# Keep bypass_hosts EMPTY (or minimal and reviewed): every entry is
# egress your inspection layer never sees. Max 100 entries.
# Health-check from the proxy's exit, optionally against a specific target.
curl -sS -X POST "https://api.onkernel.com/proxies/PROXY_ID/check" \
-H "Authorization: Bearer ${KERNEL_API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "url": "https://crm.example.com" }'
Validation & Testing
POST /proxies/{id}/check(optionally with a target URL) — confirm health from the proxy’s exit- Create a browser bound to the proxy and confirm target-site traffic appears in your proxy logs
- Confirm the API response for the proxy shows
has_password: trueand no plaintext
Expected result: All agent egress for proxied browsers transits and is logged by your proxy.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.6 | Network boundary protection |
| NIST 800-53 | SC-7 | Boundary protection |
| ISO 27001:2022 | 8.20 | Networks security |
4.4 Enable Zero Data Retention for Sensitive Workloads
Profile Level: L3 (Maximum Security) NIST 800-53: SI-12 CIS Controls v8: 3 (Data Protection)
Description
Kernel’s Zero Data Retention (ZDR) suppresses post-session persistence of three surfaces: session recordings, live-view streams, and telemetry. It is an Enterprise-only capability, is not enabled by default, and has no self-service toggle — Enterprise customers contact Kernel support to scope it, specifying which surfaces to suppress and expected volume. Enable it for workloads where browser sessions display regulated data (PHI, payment data, customer PII).
No benchmark equivalent yet.
Rationale
Why This Matters:
- Replays and telemetry are recordings of everything the agent saw — for a healthcare or finance workload, that’s a second copy of regulated data living in your vendor
- Suppressing at the platform layer is stronger than deleting after the fact; data never persisted cannot be breached later
- Pairs with the Enterprise HIPAA BAA (see Appendix A) for covered-entity use
Attack Prevented: Exposure of regulated data through retained session recordings and telemetry stores.
Prerequisites
- Active Enterprise plan
- Data-classification decision on which workloads/surfaces require suppression
ClickOps Implementation
Step 1: Scope with Kernel support
- Contact Kernel support (per the documented support path) with the surfaces to suppress — session recordings, live view, telemetry — and expected volume
- Confirm in writing which organization/projects the ZDR configuration covers
Step 2: Align internal features
- For ZDR workloads, do not start replay recordings (Control 5.3) and confirm telemetry expectations with the scoped configuration
Time to Complete: Support-led; days, not minutes
Validation & Testing
- Run a test session in a ZDR-scoped project, then attempt to retrieve its replay and telemetry
- Verify live view behavior matches the scoped suppression
Expected result: No recordings, live-view streams, or telemetry persist for suppressed surfaces.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | C1.1 / P4.2 | Confidentiality and data disposal |
| NIST 800-53 | SI-12 | Information management and retention |
| ISO 27001:2022 | 8.10 | Information deletion |
4.5 Cryptographically Sign Agent Traffic (Web Bot Auth)
Profile Level: L3 (Maximum Security) NIST 800-53: IA-9 CIS Controls v8: 13 (Network Monitoring and Defense)
Description
Web Bot Auth signs your browsers’ outbound requests with RFC 9421 HTTP message signatures, adding Signature, Signature-Input, and Signature-Agent headers that services (Cloudflare and Vercel among them) can verify against your published keys. On Kernel: generate an Ed25519 JWK, host the public key at https://yourdomain.com/.well-known/http-message-signatures-directory (JWKS), build the signing extension with kernel extensions build-web-bot-auth --key ./my-key.jwk --signature-agent https://yourdomain.com, and attach it to browsers via the extensions field.
No benchmark equivalent yet.
Rationale
Why This Matters:
- Gives target services a cryptographic way to distinguish your legitimate agents from imposters spoofing your user-agent or brand
- Establishes non-repudiable attribution for your fleet’s traffic — valuable in abuse disputes and partner integrations
- Key custody discipline applies: the JWK’s private component (
d) signs everything your fleet does
Attack Prevented: Agent impersonation — third parties running attack traffic that appears to originate from your organization’s automation.
Prerequisites
- A domain where you can publish the JWKS well-known path
- Secure storage for the private JWK
ClickOps Implementation
No dashboard console path is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# Prerequisites (per the Web Bot Auth doc):
# 1. An Ed25519 JWK file containing the public (x) AND private (d)
# components — guard the private component like a signing CA key.
# 2. Your public keys published in JWKS form at:
# https://yourdomain.com/.well-known/http-message-signatures-directory
#
# Build the signing extension. Signed requests carry RFC 9421 headers:
# Signature, Signature-Input, and Signature-Agent (your key directory URL).
kernel extensions build-web-bot-auth \
--key ./my-key.jwk \
--signature-agent https://yourdomain.com
# Attach the signing extension to fleet browsers:
kernel browsers create --extension my-web-bot-auth
# Services like Cloudflare and Vercel verify these signatures against your
# published public key, confirming the request came from YOUR agent.
Validation & Testing
- From a signing browser, request a service you control and verify the three signature headers arrive and validate against your JWKS
- Confirm the public-key directory is reachable at the well-known URL
Expected result: Requests carry valid RFC 9421 signatures traceable to your published keys.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | System identification and authentication |
| NIST 800-53 | IA-9 | Service identification and authentication |
| ISO 27001:2022 | 5.16 | Identity management |
5. Monitoring & Audit
5.1 Review Organization Audit Logs on a Cadence
Profile Level: L1 (Baseline) NIST 800-53: AU-2, AU-6 CIS Controls v8: 8 (Audit Log Management)
Description
Kernel records every authenticated API request across the organization: who called, which endpoint, when, and the outcome. Logs are searchable in the dashboard, via GET /audit-logs (30-day window per search, 100 records per page), and via kernel audit-logs search — which filters by path, user ID, email, IP, status code, or HTTP method, and excludes GETs by default (--include-get reverses that). Bulk export for archival uses kernel audit-logs download (compressed JSONL, SHA-256-verified batches). Audit log access requires a Start-Up or Enterprise plan.
Rationale
Why This Matters:
- Key abuse looks like API traffic — profile downloads, credential-connection changes, key mints — and audit logs are where it shows
- Anchor a weekly review on the write-path: key creation/rotation/deletion, project changes, proxy changes, profile downloads
- The 30-day search window means anything older must already be exported (Control 5.2) to be investigable
Attack Prevented: Undetected credential abuse and configuration tampering; loss of forensic timeline after an incident.
Prerequisites
- Start-Up or Enterprise plan
- A named owner for the review cadence
ClickOps Implementation
Step 1: Interactive review
- In the Kernel dashboard, open the audit logs view
- Filter to the last 7 days and review write-path events (key, project, proxy, profile, credential operations)
Time to Complete: ~15 minutes weekly
Code Implementation
Code Pack: CLI Script
# Search windows are capped at 30 days. GET requests are excluded by
# default (add --include-get to reverse). Anchor the weekly review on
# write-path surfaces: keys, projects, proxies, profiles, credentials.
for surface in /org/api_keys /org/projects /proxies /profiles /auth/connections; do
kernel audit-logs search \
--start "$(date -v-7d +%F 2>/dev/null || date -d '7 days ago' +%F)" \
--end "$(date +%F)" \
--search "${surface}" \
--limit 500 \
--output json
done
# --search also matches user IDs, emails, IPs, and status codes:
kernel audit-logs search \
--start "$(date -v-7d +%F 2>/dev/null || date -d '7 days ago' +%F)" \
--end "$(date +%F)" \
--search 403 --output json
# Bulk export for archival/forensics: compressed JSONL, downloaded in
# batches of up to 50,000 records with per-batch SHA-256 verification.
kernel audit-logs download \
--start "$(date -v-30d +%F 2>/dev/null || date -d '30 days ago' +%F)" \
--end "$(date +%F)" \
--to "audit-$(date +%F).jsonl.gz"
Validation & Testing
- Mint and delete a test key, then find both events in a search
- Download a 7-day export and confirm batch checksums verify
Expected result: Administrative actions appear in search within the review window; exports verify.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC7.2 | Monitoring for anomalous activity |
| NIST 800-53 | AU-6 | Audit record review and analysis |
| ISO 27001:2022 | 8.15 | Logging |
5.2 Stream Audit Logs Continuously to S3
Profile Level: L2 (Hardened) NIST 800-53: AU-9, AU-4 CIS Controls v8: 8 (Audit Log Management)
Description
Enterprise organizations can configure continuous audit-log export to an S3 bucket they own: POST /audit-logs/export/destinations with type: "s3", region, bucket, prefix, role_arn, format: "jsonl.gz", and optional kms_key_id. The destination is created paused; Kernel returns a kernel_role_arn and an external_id, you add both to your IAM role’s trust policy (sts:ExternalId condition), grant S3 (and optional KMS) permissions, run the destination test (which assumes the role and writes a probe object), and only then activate. Delivered objects are partitioned jsonl.gz under date=YYYY-MM-DD/hour=HH prefixes, and events include event_id, timestamp, method, path, and status.
Rationale
Why This Matters:
- Puts the audit trail in infrastructure the vendor cannot alter — tamper-resistant custody in your own account, under your own retention
- Escapes the 30-day interactive search window; incident response gets the full timeline
- The
external_idtrust condition is the documented defense against confused-deputy access to your bucket — do not omit it
Attack Prevented: Audit-trail loss or tampering; investigation gaps beyond the platform’s search window.
Prerequisites
- Active Enterprise plan
- An S3 bucket and IAM role you control (optionally a KMS key)
ClickOps Implementation
No dashboard console path for export destinations is documented. Use the Code implementation below.
Code Implementation
Code Pack: CLI Script
# 1. Create the destination — it starts PAUSED by design.
kernel audit-logs export create \
--region us-east-1 \
--bucket customer-audit-logs \
--prefix kernel/audit \
--role-arn arn:aws:iam::123456789012:role/kernel-audit-export
# 2. The response returns kernel_role_arn and external_id. Update YOUR
# IAM role's trust policy to allow that kernel_role_arn to assume it,
# WITH the sts:ExternalId condition set to the returned external_id —
# the documented defense against confused-deputy access to your bucket.
# Grant the role S3 write (and KMS, if the bucket is KMS-encrypted).
# 3. Test BEFORE activating: Kernel assumes the configured role and
# uploads a probe object with the same request metadata as a real delivery.
kernel audit-logs export test dest_01example
# 4. Activate once the test passes; delivery writes partitioned jsonl.gz
# objects under .../date=YYYY-MM-DD/hour=HH/ prefixes.
kernel audit-logs export resume dest_01example
# Operate: list, inspect, pause, or remove destinations.
kernel audit-logs export list
kernel audit-logs export get dest_01example
Validation & Testing
- Run the destination test and confirm the probe object lands in your bucket
- After activation, confirm partitioned objects arrive under the expected
date=/hour=layout - From your SIEM, ingest a sample and reconcile
event_ids against an interactive search
Expected result: Continuous, verified delivery into your bucket; SIEM parity with interactive search.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC7.2 | Security event capture and retention |
| NIST 800-53 | AU-9 | Protection of audit information |
| ISO 27001:2022 | 8.15 | Logging |
5.3 Capture Session Telemetry and Replays for High-Risk Automations
Profile Level: L2 (Hardened) NIST 800-53: SI-4, AU-14 CIS Controls v8: 8 (Audit Log Management)
Description
Kernel captures what happens inside a browser session. Telemetry is enabled per session via the telemetry field at browser creation: { enabled: true } turns on the default lightweight set (control, connection, system, captcha), and telemetry.browser enables deeper categories per surface (e.g., console, network, page) — consumable live over SSE or read back later. Replays are explicit MP4 recordings: kernel.browsers.replays.start(session_id) / .stop(...), with a replay_view_url and download per replay. Enable default telemetry broadly and full capture (network/console telemetry plus replays) for automations that touch sensitive systems — and skip both for ZDR-scoped workloads (Control 4.4).
Rationale
Why This Matters:
- When an agent misbehaves — or is manipulated by hostile page content — session-level evidence is the difference between “we think” and “we know”
- Network/console telemetry shows what the page actually did (requests, errors, injected script side-effects), not just what the agent intended
- Replays are plan-gated by retention (7 days on Hobbyist, 30 days on Start-Up, custom on Enterprise) — export anything you need longer
Attack Prevented: Unattributable agent actions; inability to reconstruct a prompt-injection or account-abuse incident inside a session.
Prerequisites
- Plan supporting the retention you need (see Appendix A)
- Storage/retrieval convention for downloaded replays tied to case IDs
ClickOps Implementation
No dashboard console path for telemetry configuration is documented. Use the Code implementation below.
Code Implementation
Code Pack: SDK Script
// Default set (control, connection, system, captcha) is lightweight —
// enable it broadly as the fleet baseline.
const browser = await kernel.browsers.create({
telemetry: { enabled: true },
});
// High-risk automations (sensitive targets, authenticated sessions):
// add per-category browser telemetry — console + network show what the
// page actually did, not just what the agent intended.
const sensitive = await kernel.browsers.create({
telemetry: {
browser: {
console: { enabled: true },
network: { enabled: true },
},
},
});
// Events stream live (SDK/CLI/SSE) or can be read back after the fact.
// SKIP deep capture and replays for ZDR-scoped workloads (Control 4.4).
// Replays are explicit MP4 recordings — start before the sensitive
// workflow, stop after, and archive the download with your case record.
// Retention is plan-gated (7d Hobbyist / 30d Start-Up / custom Enterprise).
const replay = await kernel.browsers.replays.start(sensitive.session_id);
// ... agent workflow runs ...
await kernel.browsers.replays.stop(replay.replay_id, { id: sensitive.session_id });
const video = await kernel.browsers.replays.download(replay.replay_id, {
id: sensitive.session_id,
});
// Each replay also carries a replay_view_url for interactive review.
Validation & Testing
- Create a session with network+console telemetry, browse a test page, and confirm events stream over SSE
- Start and stop a replay, then fetch the
replay_view_urland download the MP4
Expected result: High-risk sessions produce reviewable telemetry and video evidence.
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC7.2 | Detailed activity monitoring |
| NIST 800-53 | SI-4 | System monitoring |
| ISO 27001:2022 | 8.16 | Monitoring activities |
6. Third-Party Integration Security
6.1 Integration Risk Assessment Matrix
Use this matrix to assess integrations that connect to your Kernel organization, and targets your Kernel agents authenticate to:
| Risk Factor | Low | Medium | High |
|---|---|---|---|
| Credential Model | OAuth, user-bound, revocable | Project-scoped API key, expiring | Org-scoped static key, no expiry |
| Session Material | No profile/credential access | Read-only profile use (pools) | Profile write/download, stored credentials |
| Execution Reach | Observability only | Browser control | Shell exec in browser VMs (MCP exec_command), extension upload |
| Egress Posture | Managed proxy, allowlisted | Proxy without inspection | Direct egress, unrestricted navigation |
| Auditability | Streaming to your S3/SIEM | Interactive search only | No audit plan coverage |
Decision guidance: anything scoring High on Session Material or Execution Reach warrants the L2/L3 controls in sections 3–4 before production use.
6.2 Common Integrations and Recommended Controls
Hosted MCP Server (Claude, Cursor, VS Code, Zed, Goose, Windsurf, others)
Data Access: High — browser control, profile management, proxy management, shell execution in browser VMs Recommended Controls:
- ✅ OAuth over static keys (Control 1.5); project-scoped, expiring keys where headless (Controls 1.1, 1.2)
- ✅ Audit-log review of MCP-originated API activity (Control 5.1)
- ⚠️ MCP clients act on model output; a prompt-injected client can drive real browsers — constrain those browsers with Chrome policies (Control 4.1) and proxies (Control 4.3)
Computer-Use Frameworks (Anthropic, OpenAI, Gemini computer-use, Browser Use, Stagehand, and similar)
Data Access: High — full browser control within your sessions and profiles Recommended Controls:
- ✅ Dedicated project + project-scoped key per framework deployment (Controls 1.1, 2.1)
- ✅ URL allowlists and download restrictions on the pools they run in (Control 4.1)
- ⚠️ These agents read untrusted page content by design (OWASP GenAI LLM Top 10 2026 — LLM01 Prompt Injection, LLM08 Excessive Agency); telemetry and replays (Control 5.3) are your incident evidence
1Password (Managed Auth credential provider)
Data Access: High — retrieves login credentials at authentication time Recommended Controls:
- ✅ Vault-scoped service account, dedicated agent vault (Control 3.3)
- ✅ Review 1Password access logs alongside Kernel audit logs (Controls 3.3, 5.1)
Terraform Provider (kernel/kernel)
Data Access: Medium — manages durable configuration (projects, pools, policies) with an API key Recommended Controls:
- ✅ Dedicated expiring key for CI (Controls 1.1–1.3);
KERNEL_API_KEYvia environment, never in state-committed config - ✅ Policy-as-code review on
chrome_policychanges (Control 4.1)
7. Compliance Quick Reference
SOC 2 Trust Services Criteria Mapping
| Control ID | Kernel Control | Guide Section |
|---|---|---|
| CC6.1 | Key expiry, credential protection, profile protection | 1.2, 3.2, 3.4 |
| CC6.2 | Unused-key removal | 1.4 |
| CC6.3 | Project-scoped keys | 1.1 |
| CC6.6 | Managed egress proxies | 4.3 |
| CC6.8 | Chrome policy enforcement | 4.1 |
| CC7.2 | Audit review, S3 streaming, telemetry | 5.1, 5.2, 5.3 |
| CC8.1 | Extension vetting | 4.2 |
NIST 800-53 Rev 5 Mapping
| Control | Kernel Control | Guide Section |
|---|---|---|
| AC-2 | Unused-key removal | 1.4 |
| AC-6 | Project-scoped keys, profile access | 1.1, 3.4 |
| IA-5 | Key expiry/rotation, secrets, credentials | 1.2, 1.3, 3.1, 3.2, 3.3 |
| IA-9 | Web Bot Auth | 4.5 |
| SC-6 | Concurrency caps | 2.2 |
| SC-7 | Project isolation, egress proxies | 2.1, 4.3 |
| SC-28 | Credential and profile encryption governance | 3.2, 3.4 |
| CM-7 | Chrome policies | 4.1 |
| CM-11 | Extension control | 4.2 |
| AU-2 / AU-6 / AU-9 | Audit logging, review, protected export | 5.1, 5.2 |
| SI-4 | Session telemetry | 5.3 |
| SI-12 | Zero Data Retention | 4.4 |
ISO 27001:2022 Mapping
| Control | Kernel Control | Guide Section |
|---|---|---|
| 5.15 / 5.16 / 5.17 / 5.18 | Access scoping, identity, authentication info, access rights | 1.1–1.5, 3.2, 3.3 |
| 8.6 | Capacity management | 2.2 |
| 8.31 | Environment separation | 2.1 |
| 8.9 | Configuration management | 4.1 |
| 8.19 | Software installation control | 4.2 |
| 8.20 | Network security | 4.3 |
| 8.10 | Information deletion | 4.4 |
| 8.15 / 8.16 | Logging and monitoring | 5.1–5.3 |
Appendix A: Plan Compatibility
Security-relevant feature availability by plan (per Kernel’s pricing documentation):
| Capability | Developer | Hobbyist | Start-Up | Enterprise |
|---|---|---|---|---|
| SOC 2 report coverage | ✅ | ✅ | ✅ | ✅ |
| Audit log search & download (5.1) | ❌ | ❌ | ✅ | ✅ |
| Continuous S3 audit export (5.2) | ❌ | ❌ | ❌ | ✅ |
| Zero Data Retention (4.4) | ❌ | ❌ | ❌ | ✅ |
| HIPAA BAA | ❌ | ❌ | ❌ | ✅ |
| Browser replay retention (5.3) | ❌ | 7 days | 30 days | Custom |
All other controls in this guide (key scoping/rotation, projects, limits, secrets, credentials, profiles, Chrome policies, extensions, proxies, telemetry, Web Bot Auth) are documented as platform features without a stated plan gate — verify current gating against the pricing page, which changes faster than this guide.
Appendix B: References
Official Kernel Documentation (Tier 1 — all URLs verified 2026-08-11):
- API Keys — creation, scoping, rotation, deletion
- Projects — isolation and access scoping
- Audit Logs — search, download, S3 export
- Zero Data Retention
- Pricing & Limits — plan gates
- App Secrets
- Managed Auth: Credentials
- Browser Profiles
- 1Password Integration
- Custom Chrome Policies
- Extensions
- Custom Proxies
- Web Bot Auth
- Telemetry Overview
- Replays
- Browsers on Unikernels — isolation architecture
- CLI: API Keys · CLI: Audit Logs
- MCP Server Authentication
- Terraform Integration · Provider docs (GitHub)
- OpenAPI specification — authoritative endpoint reference
Corroborating Research (Tier 2b/3):
- OWASP GenAI LLM Top 10 2026 — LLM01 Prompt Injection, LLM08 Excessive Agency
- Push Security: Browser & Identity Attacks Matrix — session hijacking, credential theft, malicious-extension technique classes
Benchmark Coverage: No CIS Benchmark, DISA STIG, or CISA SCuBA baseline exists for Kernel (verified absent/unverifiable 2026-08-11).
Changelog
Date discipline: the
Datecolumn below and thelast_updatedvalue in the front matter must both be the date the change is committed and pushed — not the drafting date. Re-stamp both right beforegit commit(rundate +%F).
| Date | Version | Maturity | Changes | Author |
|---|---|---|---|---|
| 2026-08-11 | 0.1.0 | draft | Initial guide: 19 controls across API key scoping, project isolation, credential/profile protection, browser execution hardening, and audit — all sourced from fetch-verified Kernel first-party documentation | Claude Code (Opus 5) |
Contributing
Found an issue or want to improve this guide?
- Report outdated information: Open an issue with tag
content-outdated - Propose new controls: Open an issue with tag
new-control - Submit improvements: See Contributing Guide