# Deployment Spaces

> Registered Kubernetes clusters (EKS, GKE, AKS) on your cloud accounts that ZopNight is allowed to provision into and deploy onto.

Source: https://zop.dev/developer-docs/deployments/deployment-spaces

---

A Deployment Space is a registered Kubernetes cluster (EKS, GKE, or AKS) on one of your [cloud accounts](https://zop.dev/docs/cloud-setup/cloud-accounts) that ZopNight is allowed to provision into and deploy onto. Every provisioning job and container deployment lives inside a space.

**Info**

Spaces give platform teams a single guardrail to delegate cluster usage: register a cluster as a space (e.g. `checkout-prod-us-east-1`) and consumers get RBAC-scoped freedom to provision and deploy inside it without touching the underlying cloud account credentials.

**Info**

The old `deploy_targets` model — where every environment had its own cluster + region tuple stored on the deployer — was retired in Phase 3a. Today, clusters are first-class entities (`deployment_spaces` in the config DB) and environments reference them by `spaceId`. The old `deploy_targets` migration is still in the deployer's schema but is no longer written; the drop is scheduled for Phase 4.

**Info**

Space CRUD is served by the **config service** — it owns the `deployment_spaces` table. Cluster-side operations that need a kubeconfig (listing or creating namespaces) are served by the **deployer** at `/space-discovery/{spaceID}`. The **provisioner** calls config's `RegisterSpace` gRPC after a freshly created cluster comes up, so a cluster you provision through the Provisioning wizard appears as a space automatically.

## Step 1 — Register the cluster on the cloud account

Before a cluster can be a space, the cloud account must be authorised to mint kubeconfigs for it. This grants ZopNight read access to the cluster's discovery API and is the same registration flow used by the Add Existing Cluster wizard.

### PUT /cloud-accounts/{accountID}/clusters/{region}/{clusterName}/access

Register a cluster on a cloud account for kubeconfig minting. (config)

### DELETE /cloud-accounts/{accountID}/clusters/{region}/{clusterName}/access

Revoke cluster access. Existing spaces using the cluster will fail to deploy until re-registered. (config)

### GET /cloud-accounts/{accountID}/clusters

List clusters discovered on the cloud account (used by the wizard

## Step 2 — Manage Spaces

### GET /spaces

List Deployment Spaces visible to the caller (filtered by RBAC). (config)

```json title="Response"
{
  "data": [
    {
      "id": "spc_abc",
      "name": "checkout-prod-us-east-1",
      "provider": "aws",
      "spaceType": "kubernetes",
      "cloudAccountId": "ca_abc123",
      "status": "active",
      "config": "{\"clusterName\":\"checkout-prod\",\"region\":\"us-east-1\"}",
      "createdAt": "2026-04-15T08:00:00Z"
    }
  ]
}
```

### POST /spaces

Register an existing cluster as a Deployment Space. (config)

```bash title="Request"
curl -X POST https://zopnight.com/api/spaces \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "checkout-prod-us-east-1",
    "provider": "aws",
    "spaceType": "kubernetes",
    "cloudAccountId": "ca_abc123",
    "config": {
      "clusterName": "checkout-prod",
      "region": "us-east-1"
    }
  }'
```

On Azure, `config.resourceGroup` is required. On AWS and GCP, `config.region` is required. `spaceType` defaults to `kubernetes`.

### GET /spaces/{spaceID}

Get a single space. Sensitive cluster credentials (caCert, serviceAccountToken) are stripped from the public response. (config)

### PATCH /spaces/{spaceID}/status

Toggle a space between active and inactive — used by the UI

### DELETE /spaces/{spaceID}

Tear down a space. Existing provisioning jobs and deployments inside it must be removed first. (config)

## Namespaces on a Space

Deployments live in namespaces. The deployer discovers existing namespaces on the cluster and lets you create new ones from the wizard, so you don't have to leave the UI for `kubectl`. Both endpoints mint an ephemeral kubeconfig from the space's cloud account credentials and run against the cluster directly.

### GET /space-discovery/{spaceID}/namespaces

List namespaces on the space

```json title="Response"
{
  "data": [
    { "name": "default", "managedByZopnight": false },
    { "name": "checkout-prod", "managedByZopnight": true, "createdAt": "2026-04-15T08:00:00Z" }
  ]
}
```

### POST /space-discovery/{spaceID}/namespaces

Create a namespace on the space

```bash title="Request"
curl -X POST https://zopnight.com/api/space-discovery/spc_abc/namespaces \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "checkout-prod" }'
```

**Tip**

It's common to use one cluster (one space) with several namespaces — one per environment (e.g. `checkout-staging`, `checkout-prod`). Each environment binds the same `spaceId` to a different namespace.

## RBAC

Space access is controlled by four policies (`space:view`, `space:create`, `space:update`, `space:delete`). System roles inherit them; custom roles need explicit grants. See [Roles & Permissions](https://zop.dev/docs/administration/roles-permissions).

For everything you can run *inside* a space — provisioning clusters, datastores, networks, and catalog components — see [Provisioning](https://zop.dev/docs/operations/provisioning).
