From 85e5aa1a0cb1cddd6418cf52f0e44edf4653e9d3 Mon Sep 17 00:00:00 2001
From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Thu, 6 Aug 2026 16:41:58 -0400
Subject: [PATCH 1/2] Correct project scoping and lifecycle docs
Document ID-only request scoping, first-class SDK project options, stable project lifecycle error codes, and org-wide authorization requirements for project administration.
---
info/projects.mdx | 45 ++++++++++----------------------------
reference/cli.mdx | 2 +-
reference/cli/projects.mdx | 9 ++------
3 files changed, 15 insertions(+), 41 deletions(-)
diff --git a/info/projects.mdx b/info/projects.mdx
index 577f05f..1063e1b 100644
--- a/info/projects.mdx
+++ b/info/projects.mdx
@@ -18,14 +18,14 @@ Every organization has at least one project. Resources that existed before proje
Your organization must always have **at least one active project**. The API returns `409 Conflict` if you try to delete the last remaining project:
```json
-{ "code": "conflict", "message": "organization must have at least one project" }
+{ "code": "last_active_project", "message": "organization must have at least one project" }
```
-A project must also be empty before it can be deleted — archive or remove its active resources first.
+A project must also be empty before it can be deleted. If active resources remain, the API returns `409 Conflict` with code `project_not_empty`; archive or remove those resources and retry. Organizations without Projects enabled receive `404 Not Found` with code `projects_disabled` from project-management endpoints.
## Scoping Requests to a Project
-Pass the `X-Kernel-Project-Id` header — a project ID or name — on any API request to scope it to a specific project. Without the header (and without a project-scoped API key), requests act on your organization's **default project**: reads return the default project's resources, and writes create resources in it.
+Pass the `X-Kernel-Project-Id` header with a project ID on any API request to scope it to a specific project. Project names are not accepted in this header. Without the header (and without a project-scoped API key), requests act on your organization's **default project**: reads return the default project's resources, and writes create resources in it.
```bash
curl https://api.onkernel.com/browsers \
@@ -46,7 +46,7 @@ curl https://api.onkernel.com/browsers \
### SDK usage
-Set the header on the client so every request is scoped to the project. You can also override it per-request.
+Set the project ID on the client so every request is scoped to that project.
```typescript TypeScript
@@ -54,16 +54,10 @@ import Kernel from '@onkernel/sdk';
// Scope the whole client to a project
const kernel = new Kernel({
- defaultHeaders: { 'X-Kernel-Project-Id': 'proj_abc123' },
+ projectID: 'proj_abc123',
});
const browser = await kernel.browsers.create();
-
-// Or override per-request
-const other = await kernel.browsers.create(
- {},
- { headers: { 'X-Kernel-Project-Id': 'proj_def456' } },
-);
```
```python Python
@@ -71,15 +65,10 @@ from kernel import Kernel
# Scope the whole client to a project
kernel = Kernel(
- default_headers={"X-Kernel-Project-Id": "proj_abc123"},
+ project_id="proj_abc123",
)
browser = kernel.browsers.create()
-
-# Or override per-request
-other = kernel.browsers.create(
- extra_headers={"X-Kernel-Project-Id": "proj_def456"},
-)
```
```go Go
@@ -97,7 +86,7 @@ func main() {
// Scope the whole client to a project.
client := kernel.NewClient(
- option.WithHeader("X-Kernel-Project-Id", "proj_abc123"),
+ option.WithProjectID("proj_abc123"),
)
browser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{})
@@ -105,17 +94,6 @@ func main() {
panic(err)
}
_ = browser
-
- // Or override per-request.
- other, err := client.Browsers.New(
- ctx,
- kernel.BrowserNewParams{},
- option.WithHeader("X-Kernel-Project-Id", "proj_def456"),
- )
- if err != nil {
- panic(err)
- }
- _ = other
}
```
@@ -128,6 +106,7 @@ 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, 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`.
+- **Most project administration requires an org-wide credential.** A project-scoped key may rename its own project, but cannot create, archive, or delete projects, or change project limits.
### OAuth
@@ -137,16 +116,16 @@ OAuth tokens (used by the Kernel CLI and MCP server) are **always org-wide**. Yo
The Kernel [CLI](/reference/cli/projects) has first-class project support:
-- A global `--project ` flag scopes any command to a single project. Names are resolved case-insensitively, so `--project staging` works.
+- A global `--project ` flag scopes any command to a single project ID.
- The `KERNEL_PROJECT` environment variable does the same, so you can set it once in your shell or CI.
- A `kernel projects` command group lets you list, create, get, and delete projects, and manage per-project limit overrides.
```bash
# Scope a single command
-kernel browsers list --project staging
+kernel browsers list --project proj_abc123
# Scope every command in the shell
-export KERNEL_PROJECT=staging
+export KERNEL_PROJECT=proj_abc123
kernel apps list
# Manage projects
@@ -315,7 +294,7 @@ if err := client.Projects.Delete(ctx, "proj_abc123"); err != nil {
- You can't delete a project that still owns active resources, and you can't delete the last remaining active project in your org.
+ Project deletion is a soft delete. A project that still owns active resources returns `project_not_empty`; the final active project returns `last_active_project`.
## Concurrency Limits
diff --git a/reference/cli.mdx b/reference/cli.mdx
index cd62f33..d813df6 100644
--- a/reference/cli.mdx
+++ b/reference/cli.mdx
@@ -90,7 +90,7 @@ kernel invoke my-app action-name --payload '{"key":"value"}'
- `--version`, `-v` - Print the CLI version
- `--no-color` - Disable color output
- `--log-level ` - Set the log level (trace, debug, info, warn, error, fatal, print)
-- `--project ` - Scope the request to a specific [project](/reference/cli/projects) (also reads the `KERNEL_PROJECT` env var)
+- `--project ` - Scope the request to a specific [project](/reference/cli/projects) ID (also reads the `KERNEL_PROJECT` env var)
## JSON Output
diff --git a/reference/cli/projects.mdx b/reference/cli/projects.mdx
index 52dd269..427f521 100644
--- a/reference/cli/projects.mdx
+++ b/reference/cli/projects.mdx
@@ -6,17 +6,14 @@ Manage [Projects](/info/projects) from the CLI and scope other commands to a spe
## Scoping commands to a project
-Use the global `--project` flag (or the `KERNEL_PROJECT` environment variable) to scope any `kernel` command to a project. The flag accepts either a **project ID** or a **project name** — names are resolved case-insensitively by listing your projects.
+Use the global `--project` flag (or the `KERNEL_PROJECT` environment variable) to scope any `kernel` command to a project ID. Project names are accepted by project-management commands whose arguments say ``, but not by this global request-scoping flag.
```bash
-# Scope a single command by name
-kernel browsers list --project staging
-
# Scope by ID
kernel browsers list --project proj_abc123
# Scope via environment variable
-export KERNEL_PROJECT=staging
+export KERNEL_PROJECT=proj_abc123
kernel apps list
```
@@ -26,8 +23,6 @@ Under the hood, the flag adds the `X-Kernel-Project-Id` header to every authenti
Project-scoped API keys are already bound to a project server-side, so you don't need `--project` when using them — but if you do pass it, it must match the key's project or the request is rejected.
-If the name is ambiguous (multiple projects share it) or no match is found, the CLI returns a clear error; pass the project ID instead.
-
## Commands
### `kernel projects list`
From 68c811910bc5c3a39b074f79ba55d552adc33f79 Mon Sep 17 00:00:00 2001
From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Fri, 7 Aug 2026 11:13:39 -0400
Subject: [PATCH 2/2] Clarify project resource cleanup wording
---
info/projects.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/info/projects.mdx b/info/projects.mdx
index 1063e1b..46f9b85 100644
--- a/info/projects.mdx
+++ b/info/projects.mdx
@@ -21,7 +21,7 @@ Your organization must always have **at least one active project**. The API retu
{ "code": "last_active_project", "message": "organization must have at least one project" }
```
-A project must also be empty before it can be deleted. If active resources remain, the API returns `409 Conflict` with code `project_not_empty`; archive or remove those resources and retry. Organizations without Projects enabled receive `404 Not Found` with code `projects_disabled` from project-management endpoints.
+A project must also be empty before it can be deleted. If active resources remain, the API returns `409 Conflict` with code `project_not_empty`; delete or otherwise remove those resources and retry. Organizations without Projects enabled receive `404 Not Found` with code `projects_disabled` from project-management endpoints.
## Scoping Requests to a Project