Implementation:OpenHands OpenHands Nested Runtime HTTP Configuration
| Knowledge Sources | |
|---|---|
| Domains | Distributed_Systems, Conversation_Management |
| Last Updated | 2026-02-11 21:00 GMT |
Overview
Concrete tool for pushing configuration to a nested runtime server via HTTP POST requests, provided by the OpenHands enterprise conversation management layer.
Description
The nested runtime HTTP configuration pattern is implemented as a sequence of HTTP POST requests made from the SaasNestedConversationManager to the nested server's REST API using the httpx.AsyncClient. After a runtime container is provisioned and its HTTP server is listening, the orchestrator sends three categories of configuration data:
- POST /api/settings -- Sends the serialized application settings (agent name, LLM model identifier, sandbox configuration, security analyzer settings) as a JSON payload.
- POST /api/add-git-providers -- Registers Git provider configurations. Each provider entry includes the provider type (GitHub, GitLab), the OAuth token, and optional repository filter settings.
- POST /api/secrets -- Injects additional secret key-value pairs that the agent may need during execution (e.g., custom API keys for third-party services).
These requests are made sequentially because the settings endpoint must succeed before providers and secrets can be meaningfully processed by the nested server. The httpx.AsyncClient is configured with appropriate timeouts and the runtime's API URL as the base URL.
This is a Pattern Doc rather than a single-method API doc, as the configuration logic spans multiple HTTP calls within a section of the maybe_start_agent_loop orchestration flow.
Usage
This pattern is applied internally during conversation initiation, after runtime provisioning and token refresh, but before nested conversation creation. It is not exposed as a standalone public API.
Code Reference
Source Location
- Repository: OpenHands
- File:
enterprise/server/saas_nested_conversation_manager.py - Lines: L450-530
Signature
# This is a pattern spanning multiple HTTP calls, not a single method.
# The relevant code section uses httpx.AsyncClient:
async with httpx.AsyncClient(base_url=api_url, timeout=30.0) as client:
# Push settings
response = await client.post("/api/settings", json=settings_payload)
# Register Git providers
response = await client.post("/api/add-git-providers", json=providers_payload)
# Inject secrets
response = await client.post("/api/secrets", json=secrets_payload)
Import
import httpx
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| api_url | str | Yes | The base URL of the nested runtime's HTTP API (e.g., http://runtime-conv123:8080).
|
| settings_payload | dict | Yes | A JSON-serializable dictionary containing the application settings to push to /api/settings.
|
| providers_payload | dict | Yes | A JSON-serializable dictionary containing Git provider configurations for /api/add-git-providers.
|
| secrets_payload | dict | No | A JSON-serializable dictionary of secret key-value pairs for /api/secrets. May be omitted if no custom secrets are configured.
|
Outputs
| Name | Type | Description |
|---|---|---|
| (side effect) | None | The nested server is configured and ready to accept conversation creation requests. Each POST returns an HTTP 200 on success; non-200 responses cause the orchestrator to raise an error and abort. |
Usage Examples
Basic Usage
import httpx
api_url = runtime.api_url # e.g., "http://runtime-conv123:8080"
async with httpx.AsyncClient(base_url=api_url, timeout=30.0) as client:
# Step 1: Push application settings
settings_response = await client.post(
"/api/settings",
json={
"agent": "CodeActAgent",
"llm_model": "claude-opus-4-20250514",
"security_analyzer": "default",
},
)
settings_response.raise_for_status()
# Step 2: Register Git providers
providers_response = await client.post(
"/api/add-git-providers",
json={
"providers": [
{"type": "github", "token": "ghp_xxxx"},
]
},
)
providers_response.raise_for_status()
# Step 3: Inject secrets
secrets_response = await client.post(
"/api/secrets",
json={
"secrets": {"CUSTOM_API_KEY": "sk-xxxx"},
},
)
secrets_response.raise_for_status()