# NamespaceInfrastructure CRD Reference

Full specification of the `NamespaceInfrastructure` custom resource.

## Metadata

| Property | Value |
|----------|-------|
| API Group | `citadel.dev` |
| Kind | `NamespaceInfrastructure` |
| List Kind | `NamespaceInfrastructureList` |
| Plural | `namespaceinfrastructures` |
| Singular | `namespaceinfrastructure` |
| Short Names | `nsi`, `nsinfra` |
| Scope | Namespaced |
| Version | `v1alpha1` |

## Spec

### Required Fields

| Field | Type | Description |
|-------|------|-------------|
| `spec.targetNamespace` | string | The namespace to provision infrastructure resources in. Must exist. |

### Network Policy Configuration

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `spec.networkPolicy.defaultDeny` | boolean | `true` | Apply a default-deny CiliumNetworkPolicy |
| `spec.networkPolicy.ingressRules` | array | `[]` | Additional ingress rules for tenant-allow policy |
| `spec.networkPolicy.egressRules` | array | `[]` | Additional egress rules for tenant-allow policy |
| `spec.networkPolicy.allowedPeers` | array[string] | `[]` | Namespaces with bidirectional traffic |
| `spec.networkPolicy.allowKubeSystem` | boolean | `true` | Allow ingress from kube-system |
| `spec.networkPolicy.allowMonitoring` | boolean | `true` | Allow ingress from monitoring |

`ingressRules` and `egressRules` use CiliumNetworkPolicy rule format with `x-kubernetes-preserve-unknown-fields`.

### Resource Quota Configuration

All fields optional. If the entire `resourceQuota` section is omitted, no ResourceQuota is created.

| Field | Type | Description |
|-------|------|-------------|
| `spec.resourceQuota.cpu` | string | CPU limit, e.g., `"4"` |
| `spec.resourceQuota.memory` | string | Memory limit, e.g., `"8Gi"` |
| `spec.resourceQuota.pods` | string | Maximum pods |
| `spec.resourceQuota.storage` | string | Storage request limit, e.g., `"100Gi"` |
| `spec.resourceQuota.pvcs` | string | Maximum PersistentVolumeClaims |
| `spec.resourceQuota.custom` | object | Additional quota entries (key-value map) |

### Limit Range Configuration

All fields have defaults. If the entire `limitRange` section is omitted, no LimitRange is created.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `spec.limitRange.defaultCpu` | string | `"500m"` | Default container CPU limit |
| `spec.limitRange.defaultMemory` | string | `"512Mi"` | Default container memory limit |
| `spec.limitRange.defaultRequestCpu` | string | `"100m"` | Default container CPU request |
| `spec.limitRange.defaultRequestMemory` | string | `"128Mi"` | Default container memory request |
| `spec.limitRange.maxCpu` | string | `"4"` | Maximum container CPU |
| `spec.limitRange.maxMemory` | string | `"8Gi"` | Maximum container memory |

## Status

The status subresource is managed by the operator.

| Field | Type | Description |
|-------|------|-------------|
| `status.phase` | string (enum) | `Pending`, `Provisioning`, `Active`, or `Failed` |
| `status.ready` | boolean | Whether all resources are applied (default: `false`) |
| `status.managedResources.defaultDenyPolicy` | boolean | Default-deny CiliumNetworkPolicy exists |
| `status.managedResources.tenantPolicy` | boolean | Tenant-allow CiliumNetworkPolicy exists |
| `status.managedResources.resourceQuota` | boolean | ResourceQuota exists |
| `status.managedResources.limitRange` | boolean | LimitRange exists |
| `status.conditions` | array | Reconciliation conditions |
| `status.policyHash` | string | SHA-256 hash (first 16 chars) of network policy config for drift detection |

### Phase Lifecycle

| Phase | Meaning |
|-------|---------|
| `Pending` | Resource created, not yet reconciled |
| `Provisioning` | Reconciler is applying resources |
| `Active` | All resources applied, `ready: true` |
| `Failed` | Error during provisioning (see conditions) |

## Printer Columns

```
NAME               TARGET        PHASE    READY   AGE
team-alpha-infra   team-alpha    Active   true    5m
```

## Resources Created

For a fully populated spec, the operator creates:

| Resource | Name | API | Description |
|----------|------|-----|-------------|
| CiliumNetworkPolicy | `{target}-default-deny` | `cilium.io/v2` | Block all + allow DNS + API server |
| CiliumNetworkPolicy | `{target}-tenant-allow` | `cilium.io/v2` | Custom rules + kube-system/monitoring + intra-namespace |
| CiliumNetworkPolicy | `{target}-peer-{peer}` | `cilium.io/v2` | One per allowed peer namespace |
| ResourceQuota | `{target}-quota` | `v1` | CPU, memory, pod, storage limits |
| LimitRange | `{target}-limits` | `v1` | Container defaults and maximums |

All resources labeled: `app.kubernetes.io/managed-by: citadel-operator`

## Examples

### Minimal (Default-Deny Only)

```yaml
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: minimal-infra
  namespace: citadel-system
spec:
  targetNamespace: my-namespace
```

Creates only a default-deny CiliumNetworkPolicy (DNS + API server allowed).

### Full Namespace with Peers

```yaml
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: team-alpha-infra
  namespace: citadel-system
spec:
  targetNamespace: team-alpha
  networkPolicy:
    defaultDeny: true
    allowedPeers:
      - shared-services
      - data-pipeline
    allowKubeSystem: true
    allowMonitoring: true
    ingressRules:
      - fromEndpoints:
          - matchLabels:
              app: api-gateway
        toPorts:
          - ports:
              - port: "8080"
                protocol: TCP
    egressRules:
      - toFQDNs:
          - matchName: "api.stripe.com"
        toPorts:
          - ports:
              - port: "443"
                protocol: TCP
  resourceQuota:
    cpu: "8"
    memory: "16Gi"
    pods: "50"
    storage: "200Gi"
    pvcs: "10"
    custom:
      services: "20"
      configmaps: "30"
  limitRange:
    defaultCpu: "500m"
    defaultMemory: "512Mi"
    defaultRequestCpu: "100m"
    defaultRequestMemory: "128Mi"
    maxCpu: "4"
    maxMemory: "8Gi"
```

### Quotas Only (No Network Policies)

```yaml
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: dev-quotas
  namespace: citadel-system
spec:
  targetNamespace: dev
  networkPolicy:
    defaultDeny: false       # No network policies
  resourceQuota:
    cpu: "2"
    memory: "4Gi"
    pods: "10"
  limitRange:
    defaultCpu: "250m"
    defaultMemory: "256Mi"
    maxCpu: "2"
    maxMemory: "4Gi"
```

### Multi-Tenant Isolation

```yaml
# Team A
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: team-a-infra
  namespace: citadel-system
spec:
  targetNamespace: team-a
  networkPolicy:
    defaultDeny: true
    allowedPeers: ["shared-services"]
  resourceQuota:
    cpu: "4"
    memory: "8Gi"
    pods: "20"
---
# Team B (isolated from Team A)
apiVersion: citadel.dev/v1alpha1
kind: NamespaceInfrastructure
metadata:
  name: team-b-infra
  namespace: citadel-system
spec:
  targetNamespace: team-b
  networkPolicy:
    defaultDeny: true
    allowedPeers: ["shared-services"]
  resourceQuota:
    cpu: "4"
    memory: "8Gi"
    pods: "20"
```

Teams A and B share access to `shared-services` but cannot communicate with each other.

## Drift Detection

Every 5 minutes, the operator:

1. Recomputes the SHA-256 hash of the current network policy config
2. Compares with `status.policyHash`
3. If different, triggers a full re-reconciliation

This catches external modifications to managed resources or spec changes that weren't caught by the create/update handler.
