# Estructura de Namespaces

## Arquitectura de Namespaces

```
aiworker-cluster/
├── control-plane/          # Backend, API, MCP Server
├── agents/                 # Claude Code agent pods
├── gitea/                  # Gitea server
├── projects/
│   └── <project-name>/
│       ├── dev/            # Desarrollo continuo
│       ├── preview-*/      # Preview deployments por tarea
│       ├── staging/        # Staging environment
│       └── production/     # Production environment
└── monitoring/             # Prometheus, Grafana
```

## Namespace: control-plane

**Propósito**: Backend API, MCP Server, servicios core

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: control-plane
  labels:
    name: control-plane
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: control-plane-quota
  namespace: control-plane
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    persistentvolumeclaims: "5"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: control-plane-limits
  namespace: control-plane
spec:
  limits:
  - max:
      cpu: "2"
      memory: 4Gi
    min:
      cpu: "100m"
      memory: 128Mi
    default:
      cpu: "500m"
      memory: 512Mi
    defaultRequest:
      cpu: "250m"
      memory: 256Mi
    type: Container
```

### Servicios en control-plane

- **Backend API**: Express + Bun
- **MCP Server**: Comunicación con agentes
- **MySQL**: Base de datos
- **Redis**: Cache y colas
- **BullMQ Workers**: Procesamiento de jobs

## Namespace: agents

**Propósito**: Pods de Claude Code agents

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: agents
  labels:
    name: agents
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: agents-quota
  namespace: agents
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "50"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: agents-limits
  namespace: agents
spec:
  limits:
  - max:
      cpu: "2"
      memory: 4Gi
    min:
      cpu: "500m"
      memory: 1Gi
    default:
      cpu: "1"
      memory: 2Gi
    defaultRequest:
      cpu: "500m"
      memory: 1Gi
    type: Container
```

### Network Policy para Agents

```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: agents-network-policy
  namespace: agents
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Permitir tráfico desde control-plane
  - from:
    - namespaceSelector:
        matchLabels:
          name: control-plane
  egress:
  # Permitir salida a control-plane (MCP Server)
  - to:
    - namespaceSelector:
        matchLabels:
          name: control-plane
  # Permitir salida a gitea
  - to:
    - namespaceSelector:
        matchLabels:
          name: gitea
  # Permitir DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
  # Permitir HTTPS externo (para Claude API)
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 443
```

## Namespace: gitea

**Propósito**: Servidor Git auto-alojado

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: gitea
  labels:
    name: gitea
    environment: production
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gitea-quota
  namespace: gitea
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    persistentvolumeclaims: "2"
```

## Namespaces por Proyecto

### Estructura Dinámica

Para cada proyecto creado, se generan automáticamente 4 namespaces:

```typescript
// services/kubernetes/namespaces.ts
export async function createProjectNamespaces(projectName: string) {
  const baseName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-')

  const namespaces = [
    `${baseName}-dev`,
    `${baseName}-staging`,
    `${baseName}-production`,
  ]

  for (const ns of namespaces) {
    await k8sClient.createNamespace({
      name: ns,
      labels: {
        project: baseName,
        'managed-by': 'aiworker',
      },
    })

    // Aplicar resource quotas
    await k8sClient.applyResourceQuota(ns, {
      requests: { cpu: '2', memory: '4Gi' },
      limits: { cpu: '4', memory: '8Gi' },
    })
  }
}
```

### Namespace: project-dev

**Propósito**: Desarrollo continuo, deploy automático de main/develop

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-project-dev
  labels:
    project: my-project
    environment: dev
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: my-project-dev
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 2Gi
    limits.cpu: "2"
    limits.memory: 4Gi
    pods: "5"
```

### Namespace: preview-task-{id}

**Propósito**: Preview deployment temporal para una tarea específica

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: preview-task-abc123
  labels:
    project: my-project
    environment: preview
    task-id: abc123
    managed-by: aiworker
    ttl: "168h"  # 7 days
  annotations:
    created-at: "2026-01-19T12:00:00Z"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: preview-quota
  namespace: preview-task-abc123
spec:
  hard:
    requests.cpu: "500m"
    requests.memory: 1Gi
    limits.cpu: "1"
    limits.memory: 2Gi
    pods: "3"
```

**Limpieza automática**:
```typescript
// Cleanup job que corre diariamente
export async function cleanupOldPreviewNamespaces() {
  const allNamespaces = await k8sClient.listNamespaces()

  for (const ns of allNamespaces) {
    if (ns.metadata?.labels?.environment === 'preview') {
      const createdAt = new Date(ns.metadata.annotations?.['created-at'])
      const ageHours = (Date.now() - createdAt.getTime()) / (1000 * 60 * 60)

      if (ageHours > 168) { // 7 days
        await k8sClient.deleteNamespace(ns.metadata.name)
        logger.info(`Deleted old preview namespace: ${ns.metadata.name}`)
      }
    }
  }
}
```

### Namespace: project-staging

**Propósito**: Staging environment, testing antes de producción

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-project-staging
  labels:
    project: my-project
    environment: staging
    managed-by: aiworker
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: staging-quota
  namespace: my-project-staging
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    pods: "10"
```

### Namespace: project-production

**Propósito**: Production environment

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-project-production
  labels:
    project: my-project
    environment: production
    managed-by: aiworker
    protected: "true"
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: my-project-production
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"
---
# Pod Disruption Budget para alta disponibilidad
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-pdb
  namespace: my-project-production
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: my-project
```

## Namespace: monitoring

**Propósito**: Prometheus, Grafana, logs

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring
  labels:
    name: monitoring
    environment: production
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: monitoring-quota
  namespace: monitoring
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    persistentvolumeclaims: "10"
```

## Gestión de Namespaces desde el Backend

```typescript
// services/kubernetes/namespaces.ts
import { KubeConfig, CoreV1Api } from '@kubernetes/client-node'

export class NamespaceManager {
  private k8sApi: CoreV1Api

  constructor() {
    const kc = new KubeConfig()
    kc.loadFromDefault()
    this.k8sApi = kc.makeApiClient(CoreV1Api)
  }

  async createNamespace(name: string, labels: Record<string, string> = {}) {
    await this.k8sApi.createNamespace({
      metadata: {
        name,
        labels: {
          'managed-by': 'aiworker',
          ...labels,
        },
      },
    })
  }

  async deleteNamespace(name: string) {
    await this.k8sApi.deleteNamespace(name)
  }

  async listNamespaces(labelSelector?: string) {
    const response = await this.k8sApi.listNamespace(undefined, undefined, undefined, undefined, labelSelector)
    return response.body.items
  }

  async namespaceExists(name: string): Promise<boolean> {
    try {
      await this.k8sApi.readNamespace(name)
      return true
    } catch {
      return false
    }
  }
}
```

## Dashboard de Namespaces

En el frontend, mostrar todos los namespaces con sus recursos:

```typescript
// hooks/useNamespaces.ts
export function useNamespaces(projectId?: string) {
  return useQuery({
    queryKey: ['namespaces', projectId],
    queryFn: async () => {
      const { data } = await api.get('/namespaces', {
        params: { projectId },
      })
      return data.namespaces
    },
  })
}
```

Vista en el dashboard:
- **Mapa de namespaces** por proyecto
- **Uso de recursos** (CPU, memoria) por namespace
- **Número de pods** activos
- **Botón de cleanup** para preview namespaces antiguos
