From 2258df4ae33406fccf9240703dad9842301d1b41 Mon Sep 17 00:00:00 2001 From: Developer Date: Sat, 21 Feb 2026 12:48:40 +0000 Subject: [PATCH] docs: comprehensive documentation update for Helm chart and MCP sidecars - Added MCP sidecar configuration documentation across all docs - Migrated from kustomize to Helm-based deployment instructions - Updated Makefile with new helm-* targets, removed outdated k8s-* targets - Rewrote DEPLOYMENT.md to focus on Helm chart deployment - Transformed VARIABLES.md into complete Helm values reference - Added MCP sidecar section to README.md with configuration examples Key improvements: - Clear instructions for enabling/disabling MCP servers - Consistent Helm-based deployment throughout documentation - Comprehensive values reference with examples - Better organization for different deployment scenarios Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- CLAUDE.md | 43 ++- DEPLOYMENT.md | 720 +++++++++++++++++++++++++------------------------- Makefile | 65 +++-- README.md | 48 ++++ VARIABLES.md | 628 ++++++++++++++++++++++++------------------- 5 files changed, 836 insertions(+), 668 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7b05fe7..ae3c26f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,12 +33,14 @@ make clean # Remove volumes ### Kubernetes Deployment ```bash -make k8s-deploy # Deploy via kustomize -kubectl apply -k k8s/ # Direct kustomize apply -make k8s-delete # Tear down -make k8s-port-forward # Forward port 5800 to localhost -make k8s-logs # Stream container logs -make k8s-shell # Open interactive shell in pod +GITHUB_REPO="https://github.com/user/repo" make helm-deploy # Deploy with Helm +make helm-delete # Tear down Helm release +make helm-port-forward # Forward port 5800 to localhost +make helm-logs # Stream container logs +make helm-shell # Open interactive shell in pod + +# Or use Helm directly +helm install mydev ./chart --set name=mydev --set githubRepo=https://github.com/user/repo ``` ### Other Useful Targets @@ -86,6 +88,35 @@ Kubernetes and Flux MCP servers run as sidecar containers in the pod, inheriting Both are enabled by default and configurable via `mcpSidecars` in `values.yaml`. Playwright MCP remains an external service. +#### Enabling/Disabling MCP Servers + +To control MCP sidecars, set the `enabled` flag in your values override: + +```yaml +# Disable both MCP sidecars +mcpSidecars: + kubernetes: + enabled: false + flux: + enabled: false + +# Or selectively enable/disable +mcpSidecars: + kubernetes: + enabled: true # Keep Kubernetes MCP enabled + flux: + enabled: false # Disable Flux MCP +``` + +When deploying via Helm: +```bash +# Using --set flag +helm install my-devcontainer ./chart --set mcpSidecars.kubernetes.enabled=false --set mcpSidecars.flux.enabled=false + +# Or with a values file +helm install my-devcontainer ./chart -f custom-values.yaml +``` + ### Storage Model - `/config` — ReadWriteMany PVC (persists across pod restarts, holds user config/dotfiles) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 8549c75..9057944 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,436 +1,428 @@ # Deployment Guide -This guide provides step-by-step instructions for deploying the Antigravity Dev Container to Kubernetes. +This guide provides step-by-step instructions for deploying the Antigravity Dev Container using Helm. ## Prerequisites -- Kubernetes cluster with Gateway API support +- Kubernetes cluster (1.19+) - `kubectl` configured to access your cluster +- `helm` CLI installed (3.0+) - ReadWriteMany storage class available (e.g., `ceph-filesystem`, `nfs-client`, `efs-sc`) -- Sealed Secrets controller installed (for secret encryption) - GitHub Container Registry access (images are public) -## Required Configuration Variables +## Quick Start -Before deploying, you need to provide the following configuration: - -### 1. Storage Configuration - -**Variable:** `storageClassName` -**Location:** `k8s/statefulset.yaml` (line ~117) -**Description:** The ReadWriteMany storage class name in your cluster -**Example values:** -- `ceph-filesystem` (Rook-Ceph) -- `nfs-client` (NFS) -- `efs-sc` (AWS EFS) -- `azurefile` (Azure Files) -- `filestore` (GCP Filestore) - -**How to find your storage class:** -```bash -kubectl get storageclass -``` - -Look for a storage class that supports `ReadWriteMany` access mode. - -### 2. GitHub Repository (Required) - -**Variable:** `github-repo` -**Location:** `k8s/configmap.yaml` (line ~9) -**Description:** The GitHub repository URL to clone on container startup -**Format:** `https://github.com/username/repository` -**Example:** `https://github.com/cpfarhood/my-project` - -### 3. GitHub Token (Optional, for private repos) - -**Variable:** `github-token` -**Location:** `k8s/secrets-example.yaml` (sealed secret) -**Description:** GitHub Personal Access Token for cloning private repositories -**Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` -**Required:** Only if cloning a private repository - -**How to create a GitHub token:** -1. Go to https://github.com/settings/tokens -2. Click "Generate new token (classic)" -3. Select scopes: `repo` (for private repos) -4. Generate and copy the token - -### 4. VNC Password (Optional) - -**Variable:** `vnc-password` -**Location:** `k8s/secrets-example.yaml` (sealed secret) -**Description:** Password for accessing the VNC web interface -**Format:** Any string (recommend 12+ characters) -**Required:** Optional, but recommended for security - -### 5. Gateway Configuration (Required for external access) - -**Variables:** -- `parentRefs.name` - Your Gateway resource name -- `parentRefs.namespace` - Namespace where Gateway is deployed -- `hostnames` - Domain name for accessing the container - -**Location:** `k8s/httproute.yaml` -**Example:** -```yaml -parentRefs: -- name: cilium-gateway # Your Gateway name - namespace: kube-system # Your Gateway namespace -hostnames: -- "devcontainer.example.com" # Your domain -``` - -### 6. Namespace (Optional) - -**Variable:** `namespace` -**Location:** `k8s/kustomization.yaml` (line ~5) -**Description:** Kubernetes namespace to deploy into -**Default:** `default` -**Example:** `devcontainer`, `development`, `team-workspaces` - -### 7. Container Image (Optional) - -**Variable:** `image` -**Location:** `k8s/statefulset.yaml` (line ~32) -**Description:** Docker image to use -**Default:** `ghcr.io/cpfarhood/devcontainer:latest` -**Format:** `registry/repository:tag` - -### 8. Resource Limits (Optional) - -**Variables:** -- `resources.requests.memory` (default: `2Gi`) -- `resources.requests.cpu` (default: `1000m`) -- `resources.limits.memory` (default: `8Gi`) -- `resources.limits.cpu` (default: `4000m`) - -**Location:** `k8s/statefulset.yaml` (lines ~98-103) - -### 9. Happy Coder Configuration (Optional) - -**Variables:** -- `happy-server-url` - Custom Happy server URL -- `happy-webapp-url` - Custom Happy webapp URL - -**Location:** `k8s/configmap.yaml` (lines ~12-13, commented out) -**Default:** Uses Happy's default servers -**When to set:** Only if using a self-hosted Happy instance - -## Deployment Steps - -### Step 1: Clone the Repository +### 1. Clone the Repository ```bash git clone https://github.com/cpfarhood/devcontainer.git cd devcontainer ``` -### Step 2: Configure Storage Class +### 2. Create Secret (Optional) -Edit `k8s/statefulset.yaml` and find the `volumeClaimTemplates` section (around line 117): +For private repos or VNC password: ```bash -# Find your storage class -kubectl get storageclass - -# Edit the file -vi k8s/statefulset.yaml +kubectl create secret generic devcontainer-mydev-secrets-env \ + --from-literal=GITHUB_TOKEN='ghp_...' \ + --from-literal=VNC_PASSWORD='changeme' \ + --from-literal=ANTHROPIC_API_KEY='sk-ant-...' ``` -Change `storageClassName` to match your cluster: -```yaml -volumeClaimTemplates: -- metadata: - name: userhome - spec: - accessModes: [ "ReadWriteMany" ] - storageClassName: "ceph-filesystem" # ← Change this - resources: - requests: - storage: 10Gi -``` - -### Step 3: Configure GitHub Repository - -Edit `k8s/configmap.yaml`: +### 3. Deploy with Helm ```bash -vi k8s/configmap.yaml +# Basic deployment +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo + +# With custom storage class +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set storage.className=nfs-client + +# With cluster access for kubectl +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set clusterAccess=readwritens ``` -Set your repository URL: -```yaml -data: - github-repo: "https://github.com/yourusername/yourrepo" -``` - -### Step 4: Configure Gateway (HTTPRoute) - -Edit `k8s/httproute.yaml`: +### 4. Access the Container ```bash -# Find your Gateway -kubectl get gateway -A - -# Edit the file -vi k8s/httproute.yaml -``` - -Update with your Gateway details: -```yaml -spec: - parentRefs: - - name: your-gateway-name # ← Change this - namespace: your-gateway-namespace # ← Change this - hostnames: - - "devcontainer.yourdomain.com" # ← Change this -``` - -### Step 5: Create Secrets - -Create the secrets for GitHub token and VNC password: - -```bash -# Create the secret -kubectl create secret generic antigravity-secrets \ - --from-literal=github-token='ghp_your_token_here' \ - --from-literal=vnc-password='your_vnc_password' \ - --dry-run=client -o yaml | \ - kubeseal --format=yaml > k8s/sealedsecrets.yaml - -# Verify the sealed secret was created -cat k8s/sealedsecrets.yaml -``` - -**If you don't have Sealed Secrets controller:** - -Option 1: Install Sealed Secrets -```bash -kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml -``` - -Option 2: Use plain secrets (not recommended for production) -```bash -kubectl create secret generic antigravity-secrets \ - --from-literal=github-token='ghp_your_token_here' \ - --from-literal=vnc-password='your_vnc_password' -``` - -### Step 6: Review Configuration (Optional) - -Review and adjust optional settings: - -**Namespace:** -```bash -vi k8s/kustomization.yaml -# Change line 5: namespace: default -``` - -**Resource limits:** -```bash -vi k8s/statefulset.yaml -# Adjust lines 98-103 for your needs -``` - -### Step 7: Deploy to Kubernetes - -```bash -# Deploy everything -kubectl apply -k k8s/ - -# Or if you changed the namespace -kubectl apply -k k8s/ -n your-namespace -``` - -### Step 8: Verify Deployment - -```bash -# Check StatefulSet -kubectl get statefulset antigravity - -# Check Pod -kubectl get pods -l app=antigravity - -# Check PVC -kubectl get pvc -l app=antigravity - -# Check HTTPRoute -kubectl get httproute antigravity - -# View logs -kubectl logs antigravity-0 -``` - -### Step 9: Access the Container - -**Option A: Via HTTPRoute (external access)** -```bash -# Open in browser -open https://devcontainer.yourdomain.com -``` - -**Option B: Via Port Forward (local access)** -```bash -# Port forward to localhost -kubectl port-forward statefulset/antigravity 5800:5800 - -# Open in browser +# Port forward +kubectl port-forward deployment/devcontainer-mydev 5800:5800 open http://localhost:5800 ``` -## Configuration Summary +## Deployment Options -Here's a quick checklist of all variables you need to set: +### Using Values File -### Required Variables +Create a custom `values.yaml`: -| Variable | File | Line | Example Value | -|----------|------|------|---------------| -| `storageClassName` | `k8s/statefulset.yaml` | ~117 | `ceph-filesystem` | -| `github-repo` | `k8s/configmap.yaml` | ~9 | `https://github.com/user/repo` | -| `parentRefs.name` | `k8s/httproute.yaml` | ~8 | `cilium-gateway` | -| `parentRefs.namespace` | `k8s/httproute.yaml` | ~9 | `kube-system` | -| `hostnames` | `k8s/httproute.yaml` | ~10 | `devcontainer.example.com` | +```yaml +name: mydev +githubRepo: https://github.com/youruser/yourrepo +ide: vscode +ssh: false -### Optional Variables +# Storage +storage: + size: 32Gi + className: ceph-filesystem -| Variable | File | Line | Default | When to Change | -|----------|------|------|---------|----------------| -| `namespace` | `k8s/kustomization.yaml` | ~5 | `default` | If deploying to different namespace | -| `github-token` | Sealed secret | N/A | None | For private repos | -| `vnc-password` | Sealed secret | N/A | None | For VNC security | -| `image` | `k8s/statefulset.yaml` | ~32 | `ghcr.io/cpfarhood/devcontainer:latest` | For specific version or custom build | -| `resources.*` | `k8s/statefulset.yaml` | ~98-103 | 2Gi/8Gi RAM, 1/4 CPU | Based on workload needs | -| `happy-server-url` | `k8s/configmap.yaml` | ~12 | Default Happy server | For self-hosted Happy | -| `happy-webapp-url` | `k8s/configmap.yaml` | ~13 | Default Happy webapp | For self-hosted Happy | +# Resources +resources: + requests: + memory: "4Gi" + cpu: "2000m" + limits: + memory: "16Gi" + cpu: "8000m" + +# Kubernetes access +clusterAccess: readwritens + +# MCP sidecars +mcpSidecars: + kubernetes: + enabled: true + flux: + enabled: false +``` + +Deploy: + +```bash +helm install mydev ./chart -f values.yaml +``` + +### SSH Access Setup + +Enable SSH and add your public key: + +```bash +# Create secret with SSH key +kubectl create secret generic devcontainer-mydev-secrets-env \ + --from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...' + +# Deploy with SSH enabled +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set ssh=true + +# Connect via SSH +kubectl port-forward deployment/devcontainer-mydev 2222:22 +ssh -p 2222 user@localhost +``` + +### MCP Sidecar Configuration + +Control MCP servers for AI-assisted operations: + +```bash +# Disable all MCP sidecars +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.kubernetes.enabled=false \ + --set mcpSidecars.flux.enabled=false + +# Enable only Kubernetes MCP +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.kubernetes.enabled=true \ + --set mcpSidecars.flux.enabled=false +``` + +### Cluster Access Levels + +Configure Kubernetes RBAC permissions: + +| Value | Scope | Permissions | Use Case | +|-------|-------|-------------|----------| +| `none` | No access | None | Default, isolated development | +| `readonlyns` | Namespace | Read-only | View resources in namespace | +| `readwritens` | Namespace | Full access | Deploy apps in namespace | +| `readonly` | Cluster-wide | Read-only | View all cluster resources | +| `readwrite` | Cluster-wide | Full access | Cluster administration | + +```bash +# Example: Full access within namespace +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set clusterAccess=readwritens +``` + +## Ingress Configuration + +### Using Gateway API HTTPRoute + +Create an HTTPRoute for external access: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: devcontainer-mydev +spec: + parentRefs: + - name: your-gateway + namespace: your-gateway-namespace + hostnames: + - devcontainer.example.com + rules: + - backendRefs: + - name: devcontainer-mydev + port: 5800 +``` + +### Using Traditional Ingress + +Create an Ingress resource: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: devcontainer-mydev +spec: + rules: + - host: devcontainer.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: devcontainer-mydev + port: + number: 5800 +``` + +## Advanced Configurations + +### Custom Happy Coder Endpoints + +For self-hosted Happy instances: + +```bash +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set happyServerUrl=https://your-happy-server.com \ + --set happyWebappUrl=https://your-happy-webapp.com +``` + +### Custom Display Resolution + +```bash +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set display.width=2560 \ + --set display.height=1440 +``` + +### Different IDE Options + +```bash +# Use Google Antigravity +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set ide=antigravity + +# SSH-only mode (no GUI) +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set ide=none \ + --set ssh=true +``` + +## Helm Operations + +### List Deployments + +```bash +helm list +``` + +### Upgrade Deployment + +```bash +# Change values +helm upgrade mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/newrepo + +# Upgrade with new chart version +git pull +helm upgrade mydev ./chart +``` + +### Uninstall + +```bash +helm uninstall mydev + +# Note: PVC persists by default +kubectl delete pvc userhome-mydev +``` + +### Rollback + +```bash +# View history +helm history mydev + +# Rollback to previous version +helm rollback mydev + +# Rollback to specific revision +helm rollback mydev 3 +``` ## Troubleshooting -### Pod not starting +### Pod Not Starting -**Check events:** ```bash -kubectl describe pod antigravity-0 +# Check pod status +kubectl get pods -l app.kubernetes.io/instance=mydev + +# Describe pod for events +kubectl describe pod -l app.kubernetes.io/instance=mydev + +# Check logs +kubectl logs deployment/devcontainer-mydev ``` -**Common issues:** -- Storage class doesn't support ReadWriteMany -- PVC not binding (check storage class exists) -- Image pull errors (check image name) +### Repository Not Cloning -### Repository not cloning - -**Check logs:** ```bash -kubectl logs antigravity-0 | grep -A 10 "Repository Initialization" +# Check init logs +kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization" + +# Verify secret exists +kubectl get secret devcontainer-mydev-secrets-env + +# Check environment +kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB ``` -**Common issues:** -- Invalid GitHub URL -- Private repo without token -- Token doesn't have correct permissions +### VNC Not Accessible -### HTTPRoute not working - -**Check HTTPRoute:** ```bash -kubectl describe httproute antigravity +# Check service +kubectl get svc devcontainer-mydev +kubectl describe svc devcontainer-mydev + +# Test with port-forward +kubectl port-forward deployment/devcontainer-mydev 5800:5800 ``` -**Common issues:** -- Gateway name/namespace incorrect -- Domain not pointing to Gateway -- TLS certificate not issued +### MCP Sidecar Issues -### VNC not accessible - -**Check service:** ```bash -kubectl get svc antigravity -kubectl describe svc antigravity +# Check all containers +kubectl get pod -l app.kubernetes.io/instance=mydev -o jsonpath='{.items[0].spec.containers[*].name}' + +# Check MCP container logs +kubectl logs deployment/devcontainer-mydev -c kubernetes-mcp +kubectl logs deployment/devcontainer-mydev -c flux-mcp + +# Verify RBAC permissions +kubectl auth can-i --list --as system:serviceaccount:default:devcontainer-mydev ``` -**Port forward test:** +### Storage Issues + ```bash -kubectl port-forward antigravity-0 5800:5800 -# Try accessing http://localhost:5800 +# Check PVC +kubectl get pvc userhome-mydev +kubectl describe pvc userhome-mydev + +# Check available storage classes +kubectl get storageclass + +# Verify ReadWriteMany support +kubectl get storageclass -o yaml | grep -i accessmodes ``` -## Quick Deploy Example +## Best Practices -Complete deployment with all values filled in: +### Production Deployment + +1. **Use specific image tags** instead of `latest`: + ```bash + helm install mydev ./chart --set image.tag=v1.0.0 + ``` + +2. **Set resource limits** appropriately: + ```yaml + resources: + requests: + memory: "4Gi" + cpu: "2000m" + limits: + memory: "8Gi" + cpu: "4000m" + ``` + +3. **Enable VNC password**: + ```bash + kubectl create secret generic devcontainer-mydev-secrets-env \ + --from-literal=VNC_PASSWORD='strong-password-here' + ``` + +4. **Use dedicated namespace**: + ```bash + kubectl create namespace dev-environments + helm install mydev ./chart -n dev-environments + ``` + +5. **Configure appropriate cluster access**: + - Use `readonlyns` or `readwritens` for namespace-scoped work + - Avoid `readwrite` cluster-wide access unless necessary + +### Multi-User Deployment + +For teams, create separate deployments per user: ```bash -# 1. Set your values -STORAGE_CLASS="ceph-filesystem" -GITHUB_REPO="https://github.com/myuser/myproject" -GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -VNC_PASSWORD="my-secure-password-123" -GATEWAY_NAME="cilium-gateway" -GATEWAY_NAMESPACE="kube-system" -DOMAIN="devcontainer.example.com" +# User 1 +helm install alice-dev ./chart \ + --set name=alice-dev \ + --set githubRepo=https://github.com/alice/project -# 2. Update storage class -sed -i "s/storageClassName: .*/storageClassName: \"$STORAGE_CLASS\"/" k8s/statefulset.yaml - -# 3. Update GitHub repo -sed -i "s|github-repo: .*|github-repo: \"$GITHUB_REPO\"|" k8s/configmap.yaml - -# 4. Update Gateway -sed -i "s/- name: gateway/- name: $GATEWAY_NAME/" k8s/httproute.yaml -sed -i "s/namespace: gateway-system/namespace: $GATEWAY_NAMESPACE/" k8s/httproute.yaml -sed -i "s/antigravity.example.com/$DOMAIN/" k8s/httproute.yaml - -# 5. Create sealed secret -kubectl create secret generic antigravity-secrets \ - --from-literal=github-token="$GITHUB_TOKEN" \ - --from-literal=vnc-password="$VNC_PASSWORD" \ - --dry-run=client -o yaml | \ - kubeseal --format=yaml > k8s/sealedsecrets.yaml - -# 6. Deploy -kubectl apply -k k8s/ - -# 7. Watch deployment -kubectl get pods -l app=antigravity -w +# User 2 +helm install bob-dev ./chart \ + --set name=bob-dev \ + --set githubRepo=https://github.com/bob/project ``` -## Updates and Maintenance +### Backup and Recovery -### Updating the Image - -The image is automatically built and pushed to ghcr.io on every commit to main. - -**To use latest:** -```bash -kubectl set image statefulset/antigravity \ - antigravity=ghcr.io/cpfarhood/devcontainer:latest -``` - -**To use specific version:** -```bash -kubectl set image statefulset/antigravity \ - antigravity=ghcr.io/cpfarhood/devcontainer:v1.0.0 -``` - -### Changing Repository - -Edit the ConfigMap and restart: -```bash -kubectl edit configmap antigravity-config -# Change github-repo value -kubectl rollout restart statefulset/antigravity -``` - -### Scaling +The home directory persists on PVC. To backup: ```bash -# Scale to multiple instances (each gets own home PVC) -kubectl scale statefulset antigravity --replicas=3 +# Create backup pod +kubectl run backup --image=busybox --restart=Never --rm -i --tty \ + -- tar czf - -C /home . | gzip > home-backup.tar.gz ``` ## Support For issues or questions: - GitHub Issues: https://github.com/cpfarhood/devcontainer/issues -- Documentation: https://github.com/cpfarhood/devcontainer +- Documentation: https://github.com/cpfarhood/devcontainer \ No newline at end of file diff --git a/Makefile b/Makefile index f67ec30..9479e87 100644 --- a/Makefile +++ b/Makefile @@ -44,26 +44,40 @@ clean: stop @echo "Cleaning up..." rm -rf ./home ./workspace -# Kubernetes deployment -k8s-deploy: - @echo "Deploying to Kubernetes..." - kubectl apply -k k8s/ +# Helm deployment +RELEASE_NAME ?= mydev +NAMESPACE ?= default -k8s-delete: - @echo "Deleting from Kubernetes..." - kubectl delete -k k8s/ +helm-deploy: + @echo "Deploying with Helm (release: $(RELEASE_NAME))..." + @if [ -z "$(GITHUB_REPO)" ]; then \ + echo "ERROR: GITHUB_REPO environment variable is required"; \ + echo "Usage: GITHUB_REPO=https://github.com/user/repo make helm-deploy"; \ + exit 1; \ + fi + helm upgrade --install $(RELEASE_NAME) ./chart \ + --namespace $(NAMESPACE) \ + --set name=$(RELEASE_NAME) \ + --set githubRepo="$(GITHUB_REPO)" \ + --set image.repository=$(REGISTRY)/$(IMAGE_NAME) \ + --set image.tag=$(IMAGE_TAG) -k8s-logs: - @echo "Showing logs..." - kubectl logs -f antigravity-0 +helm-delete: + @echo "Deleting Helm release $(RELEASE_NAME)..." + helm uninstall $(RELEASE_NAME) --namespace $(NAMESPACE) + @echo "Note: PVC persists. To delete: kubectl delete pvc userhome-$(RELEASE_NAME) -n $(NAMESPACE)" -k8s-shell: - @echo "Opening shell..." - kubectl exec -it antigravity-0 -- bash +helm-logs: + @echo "Showing logs for $(RELEASE_NAME)..." + kubectl logs -f deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE) -k8s-port-forward: - @echo "Port forwarding to localhost:5800..." - kubectl port-forward antigravity-0 5800:5800 +helm-shell: + @echo "Opening shell in $(RELEASE_NAME)..." + kubectl exec -it deployment/devcontainer-$(RELEASE_NAME) -n $(NAMESPACE) -- bash + +helm-port-forward: + @echo "Port forwarding $(RELEASE_NAME) to localhost:5800..." + kubectl port-forward deployment/devcontainer-$(RELEASE_NAME) 5800:5800 -n $(NAMESPACE) # Show help help: @@ -78,24 +92,29 @@ help: @echo " stop - Stop running container" @echo " clean - Clean up containers and volumes" @echo "" - @echo "Kubernetes Targets:" - @echo " k8s-deploy - Deploy to Kubernetes" - @echo " k8s-delete - Delete from Kubernetes" - @echo " k8s-logs - Show container logs" - @echo " k8s-shell - Open shell in container" - @echo " k8s-port-forward - Port forward to localhost" + @echo "Helm/Kubernetes Targets:" + @echo " helm-deploy - Deploy with Helm chart (requires GITHUB_REPO)" + @echo " helm-delete - Delete Helm release" + @echo " helm-logs - Show container logs" + @echo " helm-shell - Open shell in container" + @echo " helm-port-forward - Port forward to localhost" @echo "" @echo "Variables:" @echo " REGISTRY - Docker registry (default: ghcr.io/cpfarhood)" @echo " IMAGE_NAME - Image name (default: antigravity)" @echo " IMAGE_TAG - Image tag (default: latest)" + @echo " RELEASE_NAME - Helm release name (default: mydev)" + @echo " NAMESPACE - Kubernetes namespace (default: default)" + @echo " GITHUB_REPO - GitHub repository URL (required for helm-deploy)" @echo "" @echo "Environment Variables for 'make run':" @echo " GITHUB_REPO - GitHub repository URL" @echo " GITHUB_TOKEN - GitHub token (optional)" @echo " VNC_PASSWORD - VNC password (optional)" @echo "" - @echo "Example:" + @echo "Examples:" @echo " make build" @echo " make push REGISTRY=ghcr.io/myuser IMAGE_TAG=v1.0" @echo " GITHUB_REPO=https://github.com/user/repo make run" + @echo " GITHUB_REPO=https://github.com/user/repo make helm-deploy" + @echo " RELEASE_NAME=alice-dev GITHUB_REPO=https://github.com/alice/project make helm-deploy" diff --git a/README.md b/README.md index ace90bc..ccbe5a5 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,54 @@ helm install mydev ./chart \ With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is created and set as the pod's `serviceAccountName`, so `kubectl` and any in-cluster API calls use it automatically. +### MCP Sidecars + +The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with Kubernetes and Flux: + +| Sidecar | Default | Purpose | +|---------|---------|---------| +| `mcpSidecars.kubernetes.enabled` | `true` | Kubernetes API access via MCP | +| `mcpSidecars.flux.enabled` | `true` | Flux GitOps operations via MCP | + +These sidecars inherit the pod's ServiceAccount RBAC permissions (controlled by `clusterAccess`). + +**Disable MCP sidecars:** +```bash +# Disable both sidecars +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.kubernetes.enabled=false \ + --set mcpSidecars.flux.enabled=false + +# Or selectively disable +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.flux.enabled=false # Disable only Flux MCP +``` + +**Custom MCP configuration:** +```yaml +# values.yaml override +mcpSidecars: + kubernetes: + enabled: true + image: + repository: quay.io/containers/kubernetes_mcp_server + tag: latest + port: 8080 + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "500m" + flux: + enabled: false # Disabled in this example +``` + ### Display and resources | Value | Default | Description | diff --git a/VARIABLES.md b/VARIABLES.md index 554d4b6..c06e38f 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -1,305 +1,383 @@ -# Configuration Variables Reference +# Helm Chart Values Reference -Quick reference for all configurable variables in this project. +Complete reference for all configurable values in the Antigravity Dev Container Helm chart. -## Required Variables +## Core Configuration -These MUST be configured before deployment: - -### Storage Class Name -- **Variable:** `storageClassName` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~117 +### name - **Type:** String -- **Description:** ReadWriteMany storage class available in your cluster -- **Example:** `ceph-filesystem`, `nfs-client`, `efs-sc` -- **How to find:** `kubectl get storageclass` - -### GitHub Repository URL -- **Variable:** `github-repo` -- **File:** `k8s/configmap.yaml` -- **Line:** ~9 -- **Type:** String (URL) -- **Description:** Repository to clone on container startup -- **Format:** `https://github.com/username/repository` -- **Example:** `https://github.com/cpfarhood/my-project` - -### Gateway Name -- **Variable:** `parentRefs[0].name` -- **File:** `k8s/httproute.yaml` -- **Line:** ~8 -- **Type:** String -- **Description:** Name of your Gateway resource -- **How to find:** `kubectl get gateway -A` - -### Gateway Namespace -- **Variable:** `parentRefs[0].namespace` -- **File:** `k8s/httproute.yaml` -- **Line:** ~9 -- **Type:** String -- **Description:** Namespace where Gateway is deployed -- **How to find:** `kubectl get gateway -A` - -### Domain Hostname -- **Variable:** `hostnames[0]` -- **File:** `k8s/httproute.yaml` -- **Line:** ~11 -- **Type:** String (FQDN) -- **Description:** Domain name for accessing the container -- **Example:** `devcontainer.example.com` - -## Optional Variables - -### GitHub Token -- **Variable:** `github-token` -- **File:** Sealed Secret -- **Type:** String (GitHub PAT) -- **Description:** Personal Access Token for private repos -- **Required:** Only for private repositories -- **Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` -- **Scopes:** `repo` - -### VNC Password -- **Variable:** `vnc-password` -- **File:** Sealed Secret -- **Type:** String -- **Description:** Password for VNC web interface -- **Required:** Recommended for security -- **Format:** Any string (12+ characters recommended) - -### Namespace -- **Variable:** `namespace` -- **File:** `k8s/kustomization.yaml` -- **Line:** ~5 -- **Type:** String -- **Description:** Kubernetes namespace for deployment -- **Default:** `default` - -### Container Image -- **Variable:** `image` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~32 -- **Type:** String (image reference) -- **Description:** Docker image to deploy -- **Default:** `ghcr.io/cpfarhood/devcontainer:latest` -- **Format:** `registry/repository:tag` - -### Memory Request -- **Variable:** `resources.requests.memory` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~99 -- **Type:** String (quantity) -- **Description:** Minimum memory to reserve -- **Default:** `2Gi` -- **Format:** `Gi` or `Mi` - -### Memory Limit -- **Variable:** `resources.limits.memory` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~102 -- **Type:** String (quantity) -- **Description:** Maximum memory allowed -- **Default:** `8Gi` -- **Format:** `Gi` or `Mi` - -### CPU Request -- **Variable:** `resources.requests.cpu` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~100 -- **Type:** String (quantity) -- **Description:** Minimum CPU to reserve -- **Default:** `1000m` (1 core) -- **Format:** `m` (millicores) or `` (cores) - -### CPU Limit -- **Variable:** `resources.limits.cpu` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~103 -- **Type:** String (quantity) -- **Description:** Maximum CPU allowed -- **Default:** `4000m` (4 cores) -- **Format:** `m` (millicores) or `` (cores) - -### Storage Size -- **Variable:** `storage` (under volumeClaimTemplates) -- **File:** `k8s/statefulset.yaml` -- **Line:** ~120 -- **Type:** String (quantity) -- **Description:** Size of home directory PVC -- **Default:** `10Gi` -- **Format:** `Gi` or `Ti` - -### Happy Server URL -- **Variable:** `happy-server-url` -- **File:** `k8s/configmap.yaml` -- **Line:** ~12 (commented) -- **Type:** String (URL) -- **Description:** Custom Happy Coder server -- **Default:** `https://api.cluster-fluster.com` -- **When to set:** Self-hosted Happy instance only - -### Happy Webapp URL -- **Variable:** `happy-webapp-url` -- **File:** `k8s/configmap.yaml` -- **Line:** ~13 (commented) -- **Type:** String (URL) -- **Description:** Custom Happy Coder webapp -- **Default:** `https://app.happy.engineering` -- **When to set:** Self-hosted Happy instance only - -### Display Width -- **Variable:** `DISPLAY_WIDTH` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~56 -- **Type:** String (number) -- **Description:** VNC display width in pixels -- **Default:** `1920` - -### Display Height -- **Variable:** `DISPLAY_HEIGHT` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~58 -- **Type:** String (number) -- **Description:** VNC display height in pixels -- **Default:** `1080` - -### User ID -- **Variable:** `USER_ID` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~51 -- **Type:** String (number) -- **Description:** UID for claude user -- **Default:** `1000` - -### Group ID -- **Variable:** `GROUP_ID` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~53 -- **Type:** String (number) -- **Description:** GID for claude user -- **Default:** `1000` - -### StatefulSet Replicas -- **Variable:** `replicas` -- **File:** `k8s/statefulset.yaml` -- **Line:** ~21 -- **Type:** Integer -- **Description:** Number of container instances -- **Default:** `1` -- **Note:** Each replica gets own home PVC - -## Environment Variables (Runtime) - -These are set at runtime, not in configuration files: - -### GITHUB_REPO -- **Type:** String (URL) -- **Description:** Repository URL (from ConfigMap) +- **Default:** `""` - **Required:** Yes -- **Source:** ConfigMap `antigravity.github-repo` +- **Description:** Instance name used to generate resource names (`devcontainer-{name}`, `userhome-{name}`) +- **Example:** `mydev`, `alice-dev`, `team-workspace` -### GITHUB_TOKEN +### githubRepo - **Type:** String -- **Description:** GitHub PAT (from Secret) -- **Required:** No (only for private repos) -- **Source:** Secret `antigravity.github-token` +- **Default:** `""` +- **Required:** Yes +- **Description:** GitHub repository URL to clone into `/workspace` +- **Example:** `https://github.com/username/repository` -### VNC_PASSWORD +### ide - **Type:** String -- **Description:** VNC password (from Secret) -- **Required:** No -- **Source:** Secret `antigravity.vnc-password` +- **Default:** `vscode` +- **Options:** `vscode`, `antigravity`, `none` +- **Description:** IDE to launch inside the container + - `vscode` — VSCode via VNC browser UI on port 5800 + - `antigravity` — Google Antigravity (VSCode fork) via VNC on port 5800 + - `none` — No IDE; useful when `ssh: true` is the sole access method -### HAPPY_SERVER_URL -- **Type:** String (URL) -- **Description:** Happy server URL (from ConfigMap) -- **Required:** No -- **Source:** ConfigMap `antigravity.happy-server-url` +### ssh +- **Type:** Boolean +- **Default:** `false` +- **Description:** Start an OpenSSH server on port 22 in addition to the IDE +- **Note:** Requires `SSH_AUTHORIZED_KEYS` in env secret for key-based login -### HAPPY_WEBAPP_URL -- **Type:** String (URL) -- **Description:** Happy webapp URL (from ConfigMap) -- **Required:** No -- **Source:** ConfigMap `antigravity.happy-webapp-url` +## Image Configuration -### HAPPY_HOME_DIR -- **Type:** String (path) -- **Description:** Happy data directory -- **Required:** No -- **Default:** `/home/claude/.happy` -- **Source:** Hardcoded in StatefulSet +### image.repository +- **Type:** String +- **Default:** `ghcr.io/cpfarhood/devcontainer` +- **Description:** Container image repository -### HAPPY_EXPERIMENTAL -- **Type:** String (boolean) -- **Description:** Enable Happy experimental features -- **Required:** No +### image.tag +- **Type:** String +- **Default:** `latest` +- **Description:** Container image tag +- **Best Practice:** Use specific version tags for production + +### image.pullPolicy +- **Type:** String +- **Default:** `Always` +- **Options:** `Always`, `IfNotPresent`, `Never` +- **Description:** Image pull policy + +## Happy Coder Configuration + +### happyServerUrl +- **Type:** String +- **Default:** `https://happy.farh.net` +- **Description:** Happy Coder server endpoint +- **When to Change:** Self-hosted Happy instance + +### happyWebappUrl +- **Type:** String +- **Default:** `https://happy-coder.farh.net` +- **Description:** Happy Coder webapp URL +- **When to Change:** Self-hosted Happy instance + +### happyHomeDir +- **Type:** String +- **Default:** `/config/userdata/.happy` +- **Description:** Happy runtime state directory (persists on PVC) + +### happyExperimental +- **Type:** String +- **Default:** `"true"` +- **Description:** Enable experimental Happy features + +## Display Configuration + +### display.width +- **Type:** String +- **Default:** `"1920"` +- **Description:** VNC display width in pixels + +### display.height +- **Type:** String +- **Default:** `"1080"` +- **Description:** VNC display height in pixels + +### secureConnection +- **Type:** String +- **Default:** `"0"` +- **Options:** `"0"`, `"1"` +- **Description:** Set to `"0"` when TLS is terminated at the gateway layer + +## User Configuration + +### userId +- **Type:** String +- **Default:** `"1000"` +- **Description:** UID for the app user + +### groupId +- **Type:** String +- **Default:** `"1000"` +- **Description:** GID for the app user + +## Storage Configuration + +### storage.size +- **Type:** String +- **Default:** `32Gi` +- **Description:** Size of the persistent home directory +- **Format:** Kubernetes quantity (e.g., `10Gi`, `100Gi`, `1Ti`) + +### storage.className +- **Type:** String +- **Default:** `ceph-filesystem` +- **Description:** StorageClass name (must support ReadWriteMany) +- **Examples:** `ceph-filesystem`, `nfs-client`, `efs-sc`, `azurefile` + +### shm.sizeLimit +- **Type:** String +- **Default:** `2Gi` +- **Description:** `/dev/shm` size (memory-backed emptyDir for Electron apps) + +## Resource Limits + +### resources.requests.memory +- **Type:** String +- **Default:** `2Gi` +- **Description:** Minimum memory to reserve +- **Format:** Kubernetes quantity + +### resources.requests.cpu +- **Type:** String +- **Default:** `1000m` +- **Description:** Minimum CPU to reserve +- **Format:** Millicores (`1000m` = 1 CPU core) + +### resources.limits.memory +- **Type:** String +- **Default:** `8Gi` +- **Description:** Maximum memory allowed +- **Format:** Kubernetes quantity + +### resources.limits.cpu +- **Type:** String +- **Default:** `4000m` +- **Description:** Maximum CPU allowed +- **Format:** Millicores (`4000m` = 4 CPU cores) + +## Kubernetes Access + +### clusterAccess +- **Type:** String +- **Default:** `none` +- **Options:** + - `none` — No cluster access + - `readonlyns` — Read-only access to release namespace + - `readwritens` — Full access to release namespace + - `readonly` — Read-only access cluster-wide + - `readwrite` — Full access cluster-wide +- **Description:** RBAC permissions for the pod's ServiceAccount + +## Secrets + +### envSecretName +- **Type:** String +- **Default:** `""` (auto-generates as `devcontainer-{name}-secrets-env`) +- **Description:** Name of existing Secret containing environment variables +- **Keys Recognized:** + - `GITHUB_TOKEN` — PAT for private repo access + - `VNC_PASSWORD` — Password for VNC web UI + - `ANTHROPIC_API_KEY` — API key for Claude + - `SSH_AUTHORIZED_KEYS` — Public keys for SSH access + +## MCP Sidecars + +### mcpSidecars.kubernetes.enabled +- **Type:** Boolean - **Default:** `true` -- **Source:** Hardcoded in StatefulSet +- **Description:** Enable Kubernetes MCP server sidecar -## Variable Groups by Use Case +### mcpSidecars.kubernetes.image.repository +- **Type:** String +- **Default:** `quay.io/containers/kubernetes_mcp_server` +- **Description:** Kubernetes MCP server image -### Minimal Deployment -Only these variables are required for basic deployment: -1. `storageClassName` -2. `github-repo` -3. `parentRefs.name` -4. `parentRefs.namespace` -5. `hostnames` +### mcpSidecars.kubernetes.image.tag +- **Type:** String +- **Default:** `latest` +- **Description:** Kubernetes MCP server image tag -### Private Repository Deployment -Add these for private repos: -1. All minimal deployment variables -2. `github-token` (sealed secret) +### mcpSidecars.kubernetes.port +- **Type:** Integer +- **Default:** `8080` +- **Description:** Port for Kubernetes MCP server -### Production Deployment -Recommended for production: -1. All private repository variables -2. `vnc-password` (sealed secret) -3. `resources.requests.*` (adjusted for workload) -4. `resources.limits.*` (adjusted for workload) -5. `namespace` (dedicated namespace) +### mcpSidecars.kubernetes.resources +- **Type:** Object +- **Default:** + ```yaml + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "500m" + ``` +- **Description:** Resource limits for Kubernetes MCP sidecar -### Multi-User Deployment -For multiple users: -1. All production deployment variables -2. `replicas` (set to number of users) -3. Larger `storage` size for home PVCs +### mcpSidecars.flux.enabled +- **Type:** Boolean +- **Default:** `true` +- **Description:** Enable Flux MCP server sidecar -## Quick Copy Templates +### mcpSidecars.flux.image.repository +- **Type:** String +- **Default:** `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` +- **Description:** Flux MCP server image + +### mcpSidecars.flux.image.tag +- **Type:** String +- **Default:** `v0.41.1` +- **Description:** Flux MCP server image tag + +### mcpSidecars.flux.port +- **Type:** Integer +- **Default:** `8081` +- **Description:** Port for Flux MCP server + +### mcpSidecars.flux.resources +- **Type:** Object +- **Default:** + ```yaml + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "500m" + ``` +- **Description:** Resource limits for Flux MCP sidecar + +## Usage Examples + +### Minimal Configuration -### Minimal Required Variables ```yaml -# k8s/statefulset.yaml -storageClassName: "CHANGE_ME" # Line ~117 - -# k8s/configmap.yaml -github-repo: "CHANGE_ME" # Line ~9 - -# k8s/httproute.yaml -parentRefs: -- name: CHANGE_ME # Line ~8 - namespace: CHANGE_ME # Line ~9 -hostnames: -- "CHANGE_ME" # Line ~11 +name: mydev +githubRepo: https://github.com/user/repo ``` -### With Secrets -```bash -kubectl create secret generic antigravity-secrets \ - --from-literal=github-token='CHANGE_ME' \ - --from-literal=vnc-password='CHANGE_ME' \ - --dry-run=client -o yaml | \ - kubeseal --format=yaml > k8s/sealedsecrets.yaml -``` +### Production Configuration -### With Resource Adjustments ```yaml -# k8s/statefulset.yaml (lines ~98-103) +name: prod-workspace +githubRepo: https://github.com/company/application +ide: vscode +ssh: true + +image: + tag: v1.0.0 + +storage: + size: 100Gi + className: ceph-filesystem + resources: requests: - memory: "CHANGE_ME" # e.g., 4Gi - cpu: "CHANGE_ME" # e.g., 2000m + memory: "4Gi" + cpu: "2000m" limits: - memory: "CHANGE_ME" # e.g., 16Gi - cpu: "CHANGE_ME" # e.g., 8000m + memory: "16Gi" + cpu: "8000m" + +clusterAccess: readwritens + +mcpSidecars: + kubernetes: + enabled: true + flux: + enabled: false ``` + +### Development Team Configuration + +```yaml +name: team-dev +githubRepo: https://github.com/team/project +ide: antigravity + +display: + width: "2560" + height: "1440" + +storage: + size: 50Gi + className: nfs-client + +clusterAccess: readonly + +happyServerUrl: https://happy.internal.company.com +happyWebappUrl: https://happy-app.internal.company.com +``` + +## Helm CLI Examples + +### Using --set Flags + +```bash +# Basic deployment +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/user/repo + +# With multiple values +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/user/repo \ + --set ide=antigravity \ + --set storage.size=50Gi \ + --set clusterAccess=readwritens \ + --set mcpSidecars.flux.enabled=false +``` + +### Using Values File + +Create `custom-values.yaml`: +```yaml +name: mydev +githubRepo: https://github.com/user/repo +storage: + size: 50Gi +clusterAccess: readwritens +``` + +Deploy: +```bash +helm install mydev ./chart -f custom-values.yaml +``` + +### Combining Methods + +```bash +helm install mydev ./chart \ + -f base-values.yaml \ + -f prod-values.yaml \ + --set githubRepo=https://github.com/user/repo \ + --set image.tag=v2.0.0 +``` + +## Value Precedence + +Values are applied in order of precedence (highest to lowest): +1. `--set` flags on command line +2. `-f` values files (later files override earlier) +3. `chart/values.yaml` defaults + +## Environment Variables + +These environment variables are set in the container based on chart values: + +| Environment Variable | Source Value | Description | +|---------------------|--------------|-------------| +| `GITHUB_REPO` | `githubRepo` | Repository to clone | +| `GITHUB_TOKEN` | Secret: `github-token` | PAT for private repos | +| `VNC_PASSWORD` | Secret: `vnc-password` | VNC access password | +| `ANTHROPIC_API_KEY` | Secret: `anthropic-api-key` | Claude API key | +| `SSH_AUTHORIZED_KEYS` | Secret: `ssh-authorized-keys` | SSH public keys | +| `HAPPY_SERVER_URL` | `happyServerUrl` | Happy server endpoint | +| `HAPPY_WEBAPP_URL` | `happyWebappUrl` | Happy webapp URL | +| `HAPPY_HOME_DIR` | `happyHomeDir` | Happy data directory | +| `HAPPY_EXPERIMENTAL` | `happyExperimental` | Experimental features | +| `DISPLAY_WIDTH` | `display.width` | VNC width | +| `DISPLAY_HEIGHT` | `display.height` | VNC height | +| `SECURE_CONNECTION` | `secureConnection` | TLS termination | +| `USER_ID` | `userId` | App user UID | +| `GROUP_ID` | `groupId` | App user GID | +| `IDE` | `ide` | IDE to launch | +| `SSH` | `ssh` | SSH server enabled | \ No newline at end of file