diff --git a/docs.json b/docs.json index 763e3d5..5991021 100644 --- a/docs.json +++ b/docs.json @@ -113,6 +113,7 @@ "auth/faq" ] }, + "info/api-keys", "browsers/file-io", "browsers/curl", "browsers/ssh", @@ -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..20194ed --- /dev/null +++ b/info/api-keys.mdx @@ -0,0 +1,135 @@ +--- +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: + +- Set `KERNEL_API_KEY` before running the SDK examples. + +API keys can be **org** or **project** scoped: + +- 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 + +Use the SDKs when your backend needs to provision keys for environments, customers, or automation jobs. + +### 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) +``` + + +## 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) +``` + + +## 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") +``` + + +## 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. + +## 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-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. | 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. -