# Certificates API

Module: `citadel_operator.k3s.certificates`

Manages cert-manager Certificate resources — YAML generation (pure) and cluster operations (list, check expiry, rotate).

## Constants

```python
VALID_ISSUERS = {"letsencrypt-staging", "letsencrypt-prod", "internal-ca", "citadel-selfsigned-bootstrap"}
DEFAULT_DURATION_HOURS = 2160       # 90 days
DEFAULT_RENEW_BEFORE_HOURS = 720    # 30 days
MIN_DURATION_HOURS = 1
MAX_DURATION_HOURS = 87600          # 10 years
```

## CertificateConfig

Frozen dataclass defining a cert-manager Certificate resource.

```python
@dataclass(frozen=True)
class CertificateConfig:
    issuer_name: str                                    # Must be in VALID_ISSUERS
    common_name: str
    dns_names: tuple[str, ...] = ()
    duration_hours: int = 2160                          # 90 days
    renew_before_hours: int = 720                       # 30 days
    is_ca: bool = False
    secret_name: str | None = None                      # Auto-generated from CN if None
    namespace: str = "default"
    private_key_algorithm: str = "ECDSA"                # "ECDSA" or "RSA"
    private_key_size: int = 256                         # 256/384 for ECDSA, 2048/4096 for RSA
    usages: tuple[str, ...] = ("server auth", "digital signature", "key encipherment")
    extra_labels: dict[str, str] = {}
```

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `effective_secret_name` | `str` | Secret name (auto-generated from CN if not set) |
| `effective_dns_names` | `tuple[str, ...]` | DNS names (falls back to `(common_name,)`) |

### Validation (`__post_init__`)

- `issuer_name` must be in `VALID_ISSUERS`
- `duration_hours` must be in `[1, 87600]`
- `renew_before_hours` must be less than `duration_hours`
- `private_key_algorithm` must be "ECDSA" or "RSA"
- ECDSA sizes: 256 or 384
- RSA sizes: 2048 or 4096

## CertificateManager

### Constructor

```python
CertificateManager(kubeconfig: str | None = None)
```

### Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `create_certificate` | `@staticmethod (config) -> dict` | Pure YAML generation (no I/O) |
| `list_certificates` | `(namespace="default") -> list[dict]` | List certificates in namespace |
| `check_expiry` | `(namespace="default") -> list[tuple[str, datetime, bool]]` | Returns `(name, notAfter, needs_renewal)` |
| `rotate_certificate` | `(name, namespace="default") -> bool` | Delete backing Secret to trigger renewal |

### Usage

```python
from citadel_operator import CertificateConfig, CertificateManager

# Generate cert-manager Certificate YAML (pure, no cluster needed)
config = CertificateConfig(
    issuer_name="internal-ca",
    common_name="my-service.default.svc.cluster.local",
    dns_names=("my-service", "my-service.default"),
    namespace="default",
)
cert_yaml = CertificateManager.create_certificate(config)
# cert_yaml is a dict suitable for kubectl apply or python client

# Cluster operations
cm = CertificateManager()

# Check what's expiring
for name, not_after, needs_renewal in cm.check_expiry(namespace="dex"):
    if needs_renewal:
        print(f"{name} expires {not_after}, rotating...")
        cm.rotate_certificate(name, namespace="dex")
```
