From 383b4d8d26261984f1450ba2c81d026fddef644c Mon Sep 17 00:00:00 2001 From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com> Date: Thu, 28 May 2026 15:03:59 -0400 Subject: [PATCH 1/4] docs: add API key management guides --- docs.json | 4 +- info/api-keys.mdx | 217 +++++++++++++++++++++++++++++++++++++ info/projects.mdx | 2 +- reference/cli.mdx | 3 + reference/cli/api-keys.mdx | 83 ++++++++++++++ reference/cli/auth.mdx | 5 +- 6 files changed, 309 insertions(+), 5 deletions(-) create mode 100644 info/api-keys.mdx create mode 100644 reference/cli/api-keys.mdx diff --git a/docs.json b/docs.json index 763e3d5..2926831 100644 --- a/docs.json +++ b/docs.json @@ -84,7 +84,8 @@ "browsers/termination", "browsers/standby", "browsers/headless", - "info/projects" + "info/projects", + "info/api-keys" ] }, { @@ -256,6 +257,7 @@ "reference/cli/browsers", "reference/cli/apps", "reference/cli/projects", + "reference/cli/api-keys", "reference/cli/mcp", "reference/cli/extensions" ] diff --git a/info/api-keys.mdx b/info/api-keys.mdx new file mode 100644 index 0000000..96fe162 --- /dev/null +++ b/info/api-keys.mdx @@ -0,0 +1,217 @@ +--- +title: "API Keys" +description: "Create, scope, rotate, and delete Kernel API keys" +--- + +An API key is the credential your server, script, or CI job uses to call Kernel without an interactive login. Treat it like a password: keep it out of client-side code, store it in a secret manager, and rotate it when access changes. + +Kernel only returns the plaintext key once, when you create it. Save the `key` value immediately. After that, Kernel only shows the masked value. + +## Before you start + +You need one existing Kernel credential to create another API key: + +- Use `kernel login` for CLI-based creation. +- Use `KERNEL_API_KEY` for SDK or REST-based creation. +- Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods. + +API keys can be **org-wide** or **project-scoped**: + +- Omit `project_id` to create an org-wide key that can access resources across your organization. +- Set `project_id` to create a key that can only access resources in that project. +- When you authenticate with a project-scoped key, you can only create another project-scoped key for the same project. + +## Create an API key + +Use the CLI when you're creating a key from your terminal. Use the SDK or REST API when your backend needs to provision keys for environments, customers, or automation jobs. + +### CLI + +```bash +kernel api-keys create \ + --name staging-ci \ + --days-to-expire 30 \ + --project-id proj_staging_9f3k \ + --output json +``` + +The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal. + + + `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making. + + +### SDKs + + +```typescript TypeScript +import Kernel from '@onkernel/sdk'; + +const kernel = new Kernel({ + apiKey: process.env.KERNEL_API_KEY, +}); + +const apiKey = await kernel.apiKeys.create({ + name: 'staging-ci', + days_to_expire: 30, + project_id: 'proj_staging_9f3k', +}); + +console.log(apiKey.key); // Save this value now. Kernel won't show it again. +console.log(apiKey.id, apiKey.masked_key); +``` + +```python Python +import os +from kernel import Kernel + +client = Kernel(api_key=os.environ["KERNEL_API_KEY"]) + +api_key = client.api_keys.create( + name="staging-ci", + days_to_expire=30, + project_id="proj_staging_9f3k", +) + +print(api_key.key) # Save this value now. Kernel won't show it again. +print(api_key.id, api_key.masked_key) +``` + + +### REST API + + +```typescript TypeScript +const response = await fetch('https://api.onkernel.com/org/api_keys', { + method: 'POST', + headers: { + Authorization: `Bearer ${process.env.KERNEL_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: 'staging-ci', + days_to_expire: 30, + project_id: 'proj_staging_9f3k', + }), +}); + +if (!response.ok) { + throw new Error(`Kernel returned ${response.status}`); +} + +const apiKey = await response.json(); +console.log(apiKey.key); +``` + +```python Python +import os +import httpx + +response = httpx.post( + "https://api.onkernel.com/org/api_keys", + headers={ + "Authorization": f"Bearer {os.environ['KERNEL_API_KEY']}", + "Content-Type": "application/json", + }, + json={ + "name": "staging-ci", + "days_to_expire": 30, + "project_id": "proj_staging_9f3k", + }, +) +response.raise_for_status() + +api_key = response.json() +print(api_key["key"]) +``` + + +## List and inspect API keys + +List keys to audit what exists. List and retrieve responses include `masked_key`, `project_id`, `project_name`, `created_by`, and expiry metadata, but they don't include the plaintext key. + + +```typescript TypeScript +for await (const apiKey of kernel.apiKeys.list({ limit: 20 })) { + console.log(apiKey.id, apiKey.name, apiKey.masked_key); +} + +const apiKey = await kernel.apiKeys.retrieve('key_01jwv4tn5m8k3q2v7x9p0a1bc2'); +console.log(apiKey.project_id, apiKey.expires_at); +``` + +```python Python +import os +from kernel import Kernel + +client = Kernel(api_key=os.environ["KERNEL_API_KEY"]) + +for api_key in client.api_keys.list(limit=20): + print(api_key.id, api_key.name, api_key.masked_key) + +api_key = client.api_keys.retrieve("key_01jwv4tn5m8k3q2v7x9p0a1bc2") +print(api_key.project_id, api_key.expires_at) +``` + + +You can do the same from the CLI: + +```bash +kernel api-keys list +kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2 +``` + +## Rename or delete an API key + +Rename a key when the owner or purpose changes. Delete a key when the workload no longer needs access. + + +```typescript TypeScript +await kernel.apiKeys.update('key_01jwv4tn5m8k3q2v7x9p0a1bc2', { + name: 'staging-ci-rotated', +}); + +await kernel.apiKeys.delete('key_01jwv4tn5m8k3q2v7x9p0a1bc2'); +``` + +```python Python +import os +from kernel import Kernel + +client = Kernel(api_key=os.environ["KERNEL_API_KEY"]) + +client.api_keys.update( + "key_01jwv4tn5m8k3q2v7x9p0a1bc2", + name="staging-ci-rotated", +) + +client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2") +``` + + +Or use the CLI: + +```bash +kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated +kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes +``` + +## Rotate a key + +Rotate by creating the replacement first, then deleting the old key after your workload has switched over. + +1. Create a new API key with the same scope. +2. Store the new plaintext key in your secret manager. +3. Deploy or restart the workload that uses `KERNEL_API_KEY`. +4. Verify the workload can call Kernel. +5. Delete the old API key. + +This keeps downtime low because the old key continues working until the new key is active everywhere. + +## Troubleshooting + +| Error | What it means | What to do | +| --- | --- | --- | +| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-wide key. | +| `401 Unauthorized` | Kernel couldn't authenticate the request. | Run `kernel login`, or set a valid `KERNEL_API_KEY`. | +| `404 Not Found` | The project doesn't exist or the caller can't access it. | Check the project ID. If you're using a project-scoped key, create keys only for that same project. | diff --git a/info/projects.mdx b/info/projects.mdx index 2a42164..9693db7 100644 --- a/info/projects.mdx +++ b/info/projects.mdx @@ -79,7 +79,7 @@ other = kernel.browsers.create( API keys can be **org-wide** or **project-scoped**. - **Existing API keys are org-wide.** They see every resource in your organization across all projects. Include an `X-Kernel-Project-Id` header to restrict a single request to one project. -- **Project-scoped API keys** can only access resources inside the project they were issued for. Create one from the **API Keys** page in the dashboard and pick the target project when generating the key. Requests made with a scoped key are automatically limited to that project — no header required. If you do send an `X-Kernel-Project-Id` header and it conflicts with the key's project, the request is rejected with `403 Forbidden`. +- **Project-scoped API keys** can only access resources inside the project they were issued for. Create one from the **API Keys** page in the dashboard, the [CLI](/reference/cli/api-keys), an SDK, or the [API keys guide](/info/api-keys), and pass the target `project_id` when generating the key. Requests made with a scoped key are automatically limited to that project — no header required. If you do send an `X-Kernel-Project-Id` header and it conflicts with the key's project, the request is rejected with `403 Forbidden`. ### OAuth diff --git a/reference/cli.mdx b/reference/cli.mdx index 5c3c4f4..edcb503 100644 --- a/reference/cli.mdx +++ b/reference/cli.mdx @@ -46,6 +46,9 @@ kernel --version Manage projects and scope commands with `--project`. + + Create, list, rename, and delete API keys. + ## Quick Start diff --git a/reference/cli/api-keys.mdx b/reference/cli/api-keys.mdx new file mode 100644 index 0000000..f1e0ce3 --- /dev/null +++ b/reference/cli/api-keys.mdx @@ -0,0 +1,83 @@ +--- +title: "API Keys" +--- + +Manage [API keys](/info/api-keys) from the CLI. + +## `kernel api-keys create` + +Create an API key. By default, the new key is org-wide. Pass `--project-id` to create a key whose own access is scoped to that project. + +```bash +kernel api-keys create \ + --name staging-ci \ + --days-to-expire 30 \ + --project-id proj_staging_9f3k \ + --output json +``` + +| Flag | Description | +|------|-------------| +| `--name ` | API key name. Required. | +| `--days-to-expire ` | Number of days until expiry, from `1` to `3650`. Omit for no expiry. | +| `--project-id ` | Create a project-scoped API key for this project. Omit for org-wide. | +| `--output json`, `-o json` | Output the raw JSON object, including the plaintext `key` on create. | + + + `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making. + + +## `kernel api-keys list` + +List API keys in the authenticated organization. API keys are masked. + +```bash +kernel api-keys list --limit 20 +``` + +| Flag | Description | +|------|-------------| +| `--limit ` | Maximum number of results to return. | +| `--offset ` | Number of results to skip. | +| `--output json`, `-o json` | Output the raw JSON array. | + +## `kernel api-keys get ` + +Show one API key by ID. The response includes the masked key and metadata, not the plaintext key. + +```bash +kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2 +``` + +| Flag | Description | +|------|-------------| +| `--output json`, `-o json` | Output the raw JSON object. | + +## `kernel api-keys update ` + +Rename an API key. + +```bash +kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated +``` + +| Flag | Description | +|------|-------------| +| `--name ` | New API key name. Required. | +| `--output json`, `-o json` | Output the raw JSON object. | + +## `kernel api-keys delete ` + +Delete an API key. + +```bash +kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes +``` + +| Flag | Description | +|------|-------------| +| `--yes`, `-y` | Skip the confirmation prompt. | + +## Aliases + +You can also use `kernel api-key`, `kernel apikeys`, or `kernel apikey`. diff --git a/reference/cli/auth.mdx b/reference/cli/auth.mdx index 12a889c..23b2771 100644 --- a/reference/cli/auth.mdx +++ b/reference/cli/auth.mdx @@ -19,10 +19,10 @@ Display authentication status, including the active user, organization, and toke Set the `KERNEL_API_KEY` environment variable to authenticate without OAuth: ```bash -export KERNEL_API_KEY= +export KERNEL_API_KEY=sk_1234abcd ``` -Create and manage API keys from the Kernel dashboard. +Create and manage API keys from the Kernel dashboard or with [`kernel api-keys`](/reference/cli/api-keys). ## Global flags The following flags are available on every CLI command: @@ -36,4 +36,3 @@ The following flags are available on every CLI command: ## Getting help - `kernel --help` — Show the top-level command list. - `kernel --help` — Display command-specific usage and options. - From cd302a5e27884f00d5a5ff7a05da517d9b0aeef2 Mon Sep 17 00:00:00 2001 From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com> Date: Thu, 28 May 2026 15:22:21 -0400 Subject: [PATCH 2/4] docs: link API key guide to CLI reference --- info/api-keys.mdx | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/info/api-keys.mdx b/info/api-keys.mdx index 96fe162..585ba5a 100644 --- a/info/api-keys.mdx +++ b/info/api-keys.mdx @@ -37,9 +37,7 @@ kernel api-keys create \ The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal. - - `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making. - +The `--project-id` flag scopes the new API key itself. For every `kernel api-keys` command, flag, output option, alias, and the difference between `--project-id` and the global `--project` flag, see the [API keys CLI reference](/reference/cli/api-keys). ### SDKs @@ -154,12 +152,7 @@ print(api_key.project_id, api_key.expires_at) ``` -You can do the same from the CLI: - -```bash -kernel api-keys list -kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2 -``` +You can also list and inspect keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags. ## Rename or delete an API key @@ -189,12 +182,7 @@ client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2") ``` -Or use the CLI: - -```bash -kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated -kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes -``` +You can also rename and delete keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags. ## Rotate a key From 3eca23f92b2edf963565e6cd88ea07c7ec4efb2c Mon Sep 17 00:00:00 2001 From: Ilyaas Yusuf Kapadia <86218345+IlyaasK@users.noreply.github.com> Date: Fri, 29 May 2026 11:44:49 -0400 Subject: [PATCH 3/4] Update info/api-keys.mdx Co-authored-by: Eric Feng --- info/api-keys.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/info/api-keys.mdx b/info/api-keys.mdx index 585ba5a..05c39f3 100644 --- a/info/api-keys.mdx +++ b/info/api-keys.mdx @@ -15,10 +15,10 @@ You need one existing Kernel credential to create another API key: - Use `KERNEL_API_KEY` for SDK or REST-based creation. - Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods. -API keys can be **org-wide** or **project-scoped**: +API keys can be **org** or **project** scoped: -- Omit `project_id` to create an org-wide key that can access resources across your organization. -- Set `project_id` to create a key that can only access resources in that project. +- Omit `project_id` to create an org-scoped key that can access resources across your organization. +- Set `project_id` to create a project-scoped key that can only access resources in that project. - When you authenticate with a project-scoped key, you can only create another project-scoped key for the same project. ## Create an API key From f975c575fd9cc1fa0075c556663fb94ab5b7b57b Mon Sep 17 00:00:00 2001 From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com> Date: Fri, 29 May 2026 11:52:15 -0400 Subject: [PATCH 4/4] Address API key docs review feedback --- docs.json | 4 +-- info/api-keys.mdx | 78 +++-------------------------------------------- 2 files changed, 6 insertions(+), 76 deletions(-) diff --git a/docs.json b/docs.json index 2926831..5991021 100644 --- a/docs.json +++ b/docs.json @@ -84,8 +84,7 @@ "browsers/termination", "browsers/standby", "browsers/headless", - "info/projects", - "info/api-keys" + "info/projects" ] }, { @@ -114,6 +113,7 @@ "auth/faq" ] }, + "info/api-keys", "browsers/file-io", "browsers/curl", "browsers/ssh", diff --git a/info/api-keys.mdx b/info/api-keys.mdx index 05c39f3..20194ed 100644 --- a/info/api-keys.mdx +++ b/info/api-keys.mdx @@ -11,9 +11,7 @@ Kernel only returns the plaintext key once, when you create it. Save the `key` v You need one existing Kernel credential to create another API key: -- Use `kernel login` for CLI-based creation. -- Use `KERNEL_API_KEY` for SDK or REST-based creation. -- Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods. +- Set `KERNEL_API_KEY` before running the SDK examples. API keys can be **org** or **project** scoped: @@ -23,21 +21,7 @@ API keys can be **org** or **project** scoped: ## Create an API key -Use the CLI when you're creating a key from your terminal. Use the SDK or REST API when your backend needs to provision keys for environments, customers, or automation jobs. - -### CLI - -```bash -kernel api-keys create \ - --name staging-ci \ - --days-to-expire 30 \ - --project-id proj_staging_9f3k \ - --output json -``` - -The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal. - -The `--project-id` flag scopes the new API key itself. For every `kernel api-keys` command, flag, output option, alias, and the difference between `--project-id` and the global `--project` flag, see the [API keys CLI reference](/reference/cli/api-keys). +Use the SDKs when your backend needs to provision keys for environments, customers, or automation jobs. ### SDKs @@ -76,54 +60,6 @@ print(api_key.id, api_key.masked_key) ``` -### REST API - - -```typescript TypeScript -const response = await fetch('https://api.onkernel.com/org/api_keys', { - method: 'POST', - headers: { - Authorization: `Bearer ${process.env.KERNEL_API_KEY}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - name: 'staging-ci', - days_to_expire: 30, - project_id: 'proj_staging_9f3k', - }), -}); - -if (!response.ok) { - throw new Error(`Kernel returned ${response.status}`); -} - -const apiKey = await response.json(); -console.log(apiKey.key); -``` - -```python Python -import os -import httpx - -response = httpx.post( - "https://api.onkernel.com/org/api_keys", - headers={ - "Authorization": f"Bearer {os.environ['KERNEL_API_KEY']}", - "Content-Type": "application/json", - }, - json={ - "name": "staging-ci", - "days_to_expire": 30, - "project_id": "proj_staging_9f3k", - }, -) -response.raise_for_status() - -api_key = response.json() -print(api_key["key"]) -``` - - ## List and inspect API keys List keys to audit what exists. List and retrieve responses include `masked_key`, `project_id`, `project_name`, `created_by`, and expiry metadata, but they don't include the plaintext key. @@ -152,8 +88,6 @@ print(api_key.project_id, api_key.expires_at) ``` -You can also list and inspect keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags. - ## Rename or delete an API key Rename a key when the owner or purpose changes. Delete a key when the workload no longer needs access. @@ -182,8 +116,6 @@ client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2") ``` -You can also rename and delete keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags. - ## Rotate a key Rotate by creating the replacement first, then deleting the old key after your workload has switched over. @@ -194,12 +126,10 @@ Rotate by creating the replacement first, then deleting the old key after your w 4. Verify the workload can call Kernel. 5. Delete the old API key. -This keeps downtime low because the old key continues working until the new key is active everywhere. - ## Troubleshooting | Error | What it means | What to do | | --- | --- | --- | -| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-wide key. | -| `401 Unauthorized` | Kernel couldn't authenticate the request. | Run `kernel login`, or set a valid `KERNEL_API_KEY`. | +| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-scoped key. | +| `401 Unauthorized` | Kernel couldn't authenticate the request. | Set a valid `KERNEL_API_KEY`. | | `404 Not Found` | The project doesn't exist or the caller can't access it. | Check the project ID. If you're using a project-scoped key, create keys only for that same project. |