Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions core/authentication.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
---
title: Authentication
description: Auth modes, HTTP upstream authorization, namespace scoping, and runtime JWT claims.
icon: "shield-check"
---

Agent Control keeps authentication and authorization provider-neutral. The server asks a
configured provider whether a request may perform an operation, then scopes all data access with
the returned `Principal`.

## Operations

Operations are stable strings. Teams map them to their own permission model.

```text
controls.read
controls.create
controls.update
controls.delete
policies.read
policies.create
policies.update
agents.read
agents.create
agents.update
evaluators.read
observability.read
observability.write
control_bindings.read
control_bindings.write
runtime.token_exchange
runtime.use
```

## Principal

Providers return a generic principal. Agent Control treats `namespace_key`, `caller_id`,
`target_type`, and `target_id` as opaque strings.

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

`namespace_key` is the tenancy boundary. Server queries filter by it, and namespace-aware foreign
keys prevent cross-namespace references.

## Auth Modes

Management auth is selected by `AGENT_CONTROL_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| `none` | No credentials required. Intended for local development only. |
| `api_key` | Validate caller credentials locally with `AGENT_CONTROL_API_KEYS` and/or `AGENT_CONTROL_ADMIN_API_KEYS`. Requires `AGENT_CONTROL_API_KEY_ENABLED=true`. `header` is accepted as a backwards-compatible alias. |
| `http_upstream` | POST each management authorization decision to `AGENT_CONTROL_AUTH_UPSTREAM_URL`. |

When `AGENT_CONTROL_AUTH_MODE` is unset, startup selects `api_key` if local API-key validation is
enabled and `none` otherwise.

Runtime auth is selected by `AGENT_CONTROL_RUNTIME_AUTH_MODE`.

| Mode | Meaning |
| --- | --- |
| unset | Use `jwt` when `AGENT_CONTROL_RUNTIME_TOKEN_SECRET` is set. Otherwise runtime requests fall through to management auth. |
| `none` | No runtime credentials required. Intended for local development only. |
| `api_key` | Validate runtime requests with the same local API-key mechanism. |
| `jwt` | Require target-bound runtime tokens minted by `/api/v1/auth/runtime-token-exchange`. |

Common combinations:

| Management | Runtime | Use case |
| --- | --- | --- |
| `api_key` | unset | Existing standalone deployments. |
| `api_key` | `jwt` | Local management keys with short-lived target-bound runtime tokens. This does not perform per-target authorization; any valid local API key can exchange for any target in the local namespace. |
| `http_upstream` | `jwt` | External identity or authorization service for management, local token verify for high-volume runtime calls. |
| `none` | `none` | Single-process local development. Do not use in production. |

## HTTP Upstream Contract

When `AGENT_CONTROL_AUTH_MODE=http_upstream`, the server sends:

```text
POST {AGENT_CONTROL_AUTH_UPSTREAM_URL}
```

```json
{
"operation": "control_bindings.write",
"context": {
"target_type": "session",
"target_id": "target-123"
}
}
```

The provider forwards inbound `X-API-Key`, `Authorization`, and `Cookie` headers. Add
deployer-specific header names with `AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS`, for
example:

```text
AGENT_CONTROL_AUTH_UPSTREAM_EXTRA_FORWARD_HEADERS=Vendor-API-Key,X-Workspace-Id
```

If `AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN` is set, it is forwarded on
`AGENT_CONTROL_AUTH_UPSTREAM_SERVICE_TOKEN_HEADER` or `X-Agent-Control-Service-Token` by default.

A successful upstream response is:

```json
{
"namespace_key": "tenant-a",
"is_admin": false,
"caller_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"expires_at": "2026-05-11T15:00:00Z"
}
```

Only `namespace_key` is always required. `target_type` and `target_id` must be returned together
when present. `expires_at` must include timezone information.

Status handling:

| Upstream status | Agent Control result |
| --- | --- |
| `200` | Parse the principal grant. |
| `401` | Authentication error. |
| `403` | Forbidden error. |
| `404` | Not found error. |
| `429` | `503` with a rate-limit detail and `Retry-After` hint when present. |
| Other statuses or upstream network errors | Fail closed with `503`. |
| Malformed `200` principal response | Fail closed with `502`. |
| `200` target grant that conflicts with request context | Fail closed with `403`. |

## Runtime JWT Claims

`/api/v1/auth/runtime-token-exchange` is a management-style request. The configured management
provider authorizes `runtime.token_exchange` for the requested target. Agent Control then mints
its own HS256 JWT with `AGENT_CONTROL_RUNTIME_TOKEN_SECRET`.

The token payload contains:

```json
{
"iss": "agent-control/server",
"domain": "runtime",
"namespace_key": "tenant-a",
"actor_id": "user-or-key-id",
"target_type": "session",
"target_id": "target-123",
"scopes": ["runtime.use"],
"iat": 1778509800,
"exp": 1778510100,
"jti": "opaque-token-id"
}
```

Verification requires the expected issuer, `domain="runtime"`, a valid signature, an unexpired
`exp`, and `runtime.use` in `scopes`. The token is accepted only for requests whose `target_type`
and `target_id` match the bound target.

The expiry is the earlier of `AGENT_CONTROL_RUNTIME_TOKEN_TTL_SECONDS` and the upstream grant's
`expires_at` when supplied. Runtime token lifetimes are capped at 86400 seconds.
3 changes: 3 additions & 0 deletions core/configuration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,9 @@ For server database configuration, use the `AGENT_CONTROL_DB_*` variables in the

Agent Control supports API key authentication for production deployments.

For provider-based management auth, HTTP upstream authorization, namespace scoping, and runtime JWT
claims, see the [Authentication reference](/core/authentication).

### Configuration

| Variable | Default | Description |
Expand Down
2 changes: 2 additions & 0 deletions docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing",
Expand DownExpand Up@@ -183,6 +184,7 @@
"pages": [
"core/reference",
"core/configuration",
"core/authentication",
"sdk/python-sdk",
"sdk/typescript-sdk",
"testing"
Expand Down
2 changes: 1 addition & 1 deletion how-to/enable-authentication.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,4 +106,4 @@ Agent Control accepts multiple comma-separated keys per variable, making zero-do
2. Your key is present in the correct variable (`AGENT_CONTROL_API_KEYS` for regular, `AGENT_CONTROL_ADMIN_API_KEYS` for admin operations)
3. The `X-API-Key` header (or SDK `api_key` argument) matches exactly — no trailing whitespace or quotes

See the [Authentication reference](/core/reference#authentication) for the full configuration table.
See the [Authentication reference](/core/authentication) for auth modes and provider contracts.
Loading