> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Hybrid Security Architecture

> The Hybrid network boundary, the outbound-only link to xpander Cloud, and exactly what leaves your VPC.

Your data and agents run in your cluster. The control plane runs in xpander Cloud. The cluster opens outbound TLS on port 443 from the Agent Controller, and xpander Cloud never connects in, so no inbound firewall rules are needed. Prompts, completions, skill data, threads, task results and skill credentials never cross. The runtime controls are on [Security Architecture](https://docs.xpander.ai/resources/security-compliance/security-architecture.md). [Air-Gapped](https://docs.xpander.ai/resources/security-compliance/air-gapped-deployments.md) removes the Cloud connection entirely.

![Diagram](https://docs.xpander.ai/images/diagrams/resources-security-compliance-security-architecture-hybrid-101868d5.svg)

## Network boundary

![AWS architecture of the xpander Hybrid deployment. Left: users reach the cluster over a corporate VPN into the private subnet, or optionally through an Internet gateway and NLB. AZ A private subnet runs EKS, the Agent Controller, the execution tier, and the AI Gateway. AZ B holds RDS, ElastiCache, and KMS. A VPC endpoint connects over PrivateLink to xpander Cloud. The deployment manager receives the cluster identity and health status and serves agent configuration and the skill schema. LLM providers and target APIs are reached from the VPC via NAT.](https://mintcdn.com/xpanderai-099931d1/OdLxX9OyFVicAV0l/images/security/hybrid-aws-architecture.svg?fit=max\&auto=format\&n=OdLxX9OyFVicAV0l\&q=85\&s=eb3da2958eba41cb60e4a858da8d5c67)

Hybrid deployment on AWS. Left: your data plane, with corporate VPN into the private subnet, or optional Internet + NLB. Right: xpander Cloud. The PrivateLink sync carries cluster identity, health status, agent configuration, and the skill schema.

| Part | What it is |
| - | - |
| **Users** | Reach the private subnet over **Corp VPN** with no public NLB ([VPN access](https://docs.xpander.ai/self-hosted/hybrid.md#option-a-vpn--corporate-network-access)), or optionally over the **Internet** through the Internet gateway and NLB. |
| **Your VPC** | Spans two Availability Zones. Public subnets hold the NLB and NAT gateways. Private subnets hold EKS (AZ A) and data stores (AZ B). |
| **xpander Cloud** | xpander Chat, identity, agent registry and the skills catalog. Agent configuration is authored here and synced into your cluster. |
| **PrivateLink** | The Agent Controller reaches Cloud through the Endpoint svc and Deploy mgr, carrying only what [leaves the installation](#data-that-leaves-the-on-prem-installation). |
| **Your models and target APIs** | Outside the VPC, reached via NAT |

The two outbound paths to the control plane:

| Option | Target | Notes |
| - | - | - |
| Public TLS | `15.197.85.80`, `166.117.85.46` | Encrypted over public internet. Endpoint: `https://deployment-manager.xpander.ai` |
| AWS PrivateLink | `com.amazonaws.vpce.us-west-2.vpce-svc-0101884b32f655197` | AWS private backbone. Private DNS: `deployment-manager-privatelink.xpander.ai`. Endpoint security group allows TCP 443 from your VPC CIDR. See [PrivateLink setup](https://docs.xpander.ai/self-hosted/hybrid.md#privatelink). |

Supported PrivateLink regions: `us-west-2`, `us-west-1`, `eu-west-2`, `eu-central-1`, `ap-south-1`. The service itself is in `us-west-2` (pass `--service-region us-west-2` when creating the VPC endpoint from another region).

## Authentication flow

Sign-in is the only flow from the browser to xpander Cloud. It carries identity, not work.

![Authentication flow. An AI Engineer signs in at xpander Cloud (xpander Chat, SSO). The console returns the cluster address. From then on chat, tasks, and results travel between the browser and the cluster's Chat and API services, backed by the cluster's PostgreSQL.](https://mintcdn.com/xpanderai-099931d1/OdLxX9OyFVicAV0l/images/security/auth-flow.svg?fit=max\&auto=format\&n=OdLxX9OyFVicAV0l\&q=85\&s=735d22995d257829ec770032fecb9472)

Sign-in to xpander Chat. Identity goes to xpander Cloud. Everything after sign-in is browser ↔ your cluster.

The user signs in at `chat.xpander.ai` ([SSO over OIDC](https://docs.xpander.ai/resources/security-compliance/access-control.md#single-sign-on) on enterprise plans). xpander Chat then points the browser at your cluster, and chat, tasks and results move between the browser and your cluster only.

## Agent runtime flow

The prompt path runs inside your cluster.

![Agent runtime flow. A user reaches Chat and API in the cluster, then the Agent Controller, the execution tier, and AI Gateway. The execution tier calls LLM providers via NAT; the AI Gateway calls target APIs via NAT. Threads, tasks, and results are stored in the cluster's PostgreSQL. xpander Cloud is shown off to the side: not on this path, no prompts, no skill data.](https://mintcdn.com/xpanderai-099931d1/2LfxdiJcpJ2td8st/images/security/agent-runtime-flow.svg?fit=max\&auto=format\&n=2LfxdiJcpJ2td8st\&q=85\&s=d3efea0f4c61fa5978cb645392e694df)

Prompt flow. The task pipeline runs in your cluster. Model and skill calls leave via NAT. xpander Cloud is not on this path.

* The prompt enters through your ingress to Chat/API, then the Agent Controller and the execution tier (`agent-worker`). Agents run on Claude Code, Codex or OpenCode on the executor fleet.
* Model calls go through the in-cluster [LLM Gateway](https://docs.xpander.ai/run/llm-gateway/self-hosted.md), always on, with the organization's or the install's key. A call with neither key is refused with a message that names it. New runs wait for a healthy gateway.
* The skill relay calls target APIs. Model and skill calls leave your VPC via NAT, never through xpander Cloud.
* Threads, task state and results go to your cluster's PostgreSQL.

## Data that leaves the on-prem installation

Everything the Hybrid data plane sends to xpander Cloud, using only documented fields and public shapes. See also [Hybrid deployment](https://docs.xpander.ai/self-hosted/hybrid.md), [Deployment options](https://docs.xpander.ai/self-hosted/index.md), [Air-gapped deployment](https://docs.xpander.ai/self-hosted/airgap.md) and [access control](https://docs.xpander.ai/resources/security-compliance/access-control.md).

### What does not leave

| Data | Where it stays |
| - | - |
| Agent invocations and task payloads | Client → your Agent Controller / API |
| Chat threads and task activity | PostgreSQL in your cluster |
| LLM Gateway request records for your agents' model calls | Your cluster. Coding tools that people point at `https://llm-gw.xpander.ai` are recorded in xpander cloud ([LLM Gateway](https://docs.xpander.ai/run/llm-gateway.md#cloud-hybrid-and-air-gapped)) |
| Knowledge-base file uploads and queries | Your vector store |
| Skill results and target-system responses | The skill relay and the execution tier in your cluster |
| Credentials, connector OAuth tokens, LLM API keys, IAM session credentials | Kubernetes secrets (and optional AWS Secrets Manager in your account). Per-person MCP sign-in tokens are the exception: see [below](#per-person-mcp-sign-in-tokens) |
| Customer content (messages, documents, embeddings) | PostgreSQL / Redis / PVCs on encrypted EBS |
| Audit logs of skill calls, data access, and configuration changes | PostgreSQL in your cluster. See [Access Control](https://docs.xpander.ai/resources/security-compliance/access-control.md#the-record). |

### How the cluster talks to Cloud

Only the **Agent Controller** reaches xpander Cloud. It authenticates with `organization_id` + `environment_id` + the deployment-manager API key, reports heartbeats, and pulls agent, skill and MCP configuration. Other pods (`agent-worker`, gateway, API, MCP, chat) never call Cloud. Each [location](https://docs.xpander.ai/run/locations.md) also has feature toggles ([Verify the Connection](https://docs.xpander.ai/self-hosted/hybrid.md#3-verify-the-connection)), which carry no runtime payloads.

### 1. Cluster identification

Sent on every control-plane request.

| Field | Where it is set | Leaves the cluster? |
| - | - | - |
| `organization_id` | Installer `--org` / Helm `global.organizationId` | Yes, a UUID |
| `environment_id` | Installer `--env` / Helm `global.environmentId` | Yes, a UUID |
| Deployment manager API key | Installer `--dm-api-key`, stored as `DEPLOYMENT_MANAGER_API_KEY` | Yes, presented as `x-api-key`. Rotate if exposed. |

```json theme={"dark"}
{
  "organization_id": "3f2a9c18-6b41-4e0d-9a55-0c8e1b7d2f30",
  "environment_id": "9d4e21aa-8c77-4b12-a601-5f3c0e9b18d4"
}
```

```http theme={"dark"}
GET https://deployment-manager.xpander.ai/...
x-api-key: <DEPLOYMENT_MANAGER_API_KEY>
```

The installer never logs the API key. Helm writes typed secrets to a mode-600 values file that is removed on exit.

### 2. Health status (heartbeats)

Heartbeats drive **Connection established** and per-service status in **Platform > Agents > Runtime** > **Locations**. They use the same credentials. The **schema is not published**, so the example only restates what xpander Chat shows, not the wire format:

```json theme={"dark"}
{
  "organization_id": "3f2a9c18-6b41-4e0d-9a55-0c8e1b7d2f30",
  "environment_id": "9d4e21aa-8c77-4b12-a601-5f3c0e9b18d4",
  "comment": "Per-service heartbeat status is visible in the location's Components tab in the xpander app. Request and response fields are not published."
}
```

Heartbeats do not include chat text, skill results, knowledge-base content, or credentials.

### 3. Agent metadata (stored in Cloud, pulled by the cluster)

Agent definitions are authored in xpander Cloud and pulled as catalog metadata, not invocations. The shape is the public [List Agents](https://docs.xpander.ai/api-reference/v1/agents/list-agents.md) response, redacted:

```json theme={"dark"}
{
  "items": [
    {
      "id": "7c4e1a90-2b33-4f18-9d6a-11e0c8a4b275",
      "name": "PR triage",
      "description": "Lists and labels pull requests in the platform repository",
      "icon": "🔎",
      "status": "ACTIVE",
      "organization_id": "3f2a9c18-6b41-4e0d-9a55-0c8e1b7d2f30",
      "deployment_type": "serverless",
      "created_at": "2026-03-12T14:02:11.104Z",
      "created_by": "b8e0d41f-0c22-4a91-9e17-6d2f0a11c883",
      "created_by_details": {
        "id": "b8e0d41f-0c22-4a91-9e17-6d2f0a11c883",
        "name": "Alex Rivera",
        "email": "alex.rivera@example.com"
      },
      "instructions": {
        "role": ["You are a pull-request triage assistant"],
        "goal": ["Label incoming PRs and summarize risk"],
        "general": "Only use the Jira and GitHub operations attached to this agent."
      },
      "model_provider": "anthropic",
      "model_name": "claude-sonnet-4-5",
      "framework": "external-harness",
      "type": "regular",
      "llm_api_base": null,
      "has_pending_changes": false
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 20,
  "total_pages": 1
}
```

| Field | Meaning |
| - | - |
| `id`, `name`, `description`, `icon`, `status` | Agent identity and deployment status (`ACTIVE` / `INACTIVE`) |
| `organization_id` | Owning org |
| `deployment_type` | `serverless` or `null` |
| `created_by` / `created_by_details` | xpander Chat user id, display name, email |
| `instructions` | Role, goal, and general instructions configured in xpander Chat |
| `model_provider`, `model_name`, `llm_api_base` | Which model the agent is configured to use, not the prompt or completion |
| `framework`, `type` | Harness (`external-harness`) and agent type (`manager`, `regular`, …) |

The request carries only the cluster identification headers.

### 4. Skill schema (pulled)

Each skill operation is pulled as a function-call definition: name, description and parameter schema, never the tokens. Connector tokens stay in Kubernetes secrets (per-person MCP tokens: [below](#per-person-mcp-sign-in-tokens)). Redacted Jira example:

```json theme={"dark"}
{
  "type": "function",
  "function": {
    "name": "list_issues",
    "description": "List issues in a Jira project",
    "parameters": {
      "type": "object",
      "properties": {
        "query_params": {
          "type": "object",
          "description": "Query parameters to include in the request to the tool",
          "additionalProperties": false,
          "properties": {
            "jql": { "type": "string", "description": "JQL query" },
            "maxResults": { "type": "integer" }
          },
          "required": ["jql"]
        },
        "path_params": {
          "type": "object",
          "description": "Path parameters to include in the request to the tool",
          "additionalProperties": false,
          "properties": {},
          "required": []
        },
        "body_params": {
          "type": "object",
          "description": "Body parameters to include in the request payload to the tool",
          "additionalProperties": false,
          "properties": {},
          "required": []
        }
      },
      "required": ["query_params", "path_params", "body_params"]
    }
  }
}
```

| Field | Meaning |
| - | - |
| `function.name` | The operation on the agent's surface (for example `list_issues`, not org-admin APIs) |
| `function.description` | What the operation does. Shown to the model. |
| `parameters.query_params` / `path_params` / `body_params` | JSON Schema for the arguments the model may fill in |

Preset skill parameters are stripped before storage, so the definition is the call shape only.

### 5. MCP configuration (pulled)

MCP endpoints are pulled with the agent definition. Attach payloads from [`Add Agent Tool`](https://docs.xpander.ai/api-reference/v1/agents/tools/add-agent-tool):

```json theme={"dark"}
{
  "type": "mcp",
  "mcp_id": "6e2c0b14-9a88-4d21-b705-0f3a91c8e442"
}
```

```json theme={"dark"}
{
  "type": "mcp",
  "name": "internal-docs",
  "url": "https://mcp.internal.example.com",
  "transport": "streamable-http"
}
```

| Field | Meaning |
| - | - |
| `mcp_id` | Organization MCP registry entry |
| `name` | Display name for an inline server |
| `url` | Server URL (your endpoint, not xpander Cloud) |
| `transport` | `streamable-http`, `sse`, or `stdio` |

MCP **calls and results** run from your cluster's MCP service straight to your server, including servers with [per-person sign-in](https://docs.xpander.ai/run/integrations/mcp-per-person-sign-in.md). Only the person's token comes from xpander Cloud.

### Per-person MCP sign-in tokens

On Hybrid, [per-person sign-in for MCP connectors](https://docs.xpander.ai/run/integrations/mcp-per-person-sign-in.md) runs in xpander Cloud. The chart has no sign-in service.

| Step | Where it runs | What crosses to xpander Cloud |
| - | - | - |
| The person signs in | The person's browser, your identity provider and xpander Cloud's sign-in service (`client-auth.xpander.ai`) | The authorization code and the tokens your identity provider issues |
| Token storage and refresh | xpander Cloud's vault, keyed by organization, server and person | Cloud calls your identity provider's token endpoint to refresh |
| A run needs the token | The Agent Controller asks xpander Cloud for the person's current access token | The access token, Cloud to cluster |
| The MCP call | Your cluster, straight to your MCP server | Nothing. Tool arguments and results stay in your cluster |

xpander Cloud therefore holds each person's access and refresh tokens for your identity provider. If your IdP or MCP connector must not be internet-reachable, or tokens must stay in your network, use [Air-Gapped](https://docs.xpander.ai/self-hosted/airgap.md).

### 6. xpander Chat sign-in identity (browser → Cloud)

Sign-in is browser to Cloud, not cluster egress (see [Authentication flow](#authentication-flow)). Cloud holds this for each user, the same `created_by_details` shape as agent metadata:

```json theme={"dark"}
{
  "id": "b8e0d41f-0c22-4a91-9e17-6d2f0a11c883",
  "name": "Alex Rivera",
  "email": "alex.rivera@example.com"
}
```

## Next steps

> **[Review the runtime controls](https://docs.xpander.ai/resources/security-compliance/security-architecture.md)**
>
> Permissions, credentials, safety

> **[Install a Hybrid deployment](https://docs.xpander.ai/self-hosted/hybrid.md)**
>
> Installer and PrivateLink

> **[Compare with Air-Gapped](https://docs.xpander.ai/resources/security-compliance/air-gapped-deployments.md)**
>
> Zero egress

> **[Set up access control](https://docs.xpander.ai/resources/security-compliance/access-control.md)**
>
> RBAC, SSO, audit


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.