# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Overview This repository contains the Wild Cloud apps directory - a collection of Kubernetes applications packaged as Kustomize configurations. Each app is a self-contained directory with standardized manifests that can be deployed to Wild Cloud clusters using Wild Cloud CLI tools. ## Repository Architecture ### App Structure Each app follows a strict structure: - **`manifest.yaml`** - App metadata, dependencies, default configuration, and secret requirements - **`kustomization.yaml`** - Kustomize configuration with standard Wild Cloud labels - **Resource files** - Kubernetes objects (deployments, services, ingresses, PVCs, jobs, etc.) ### Templating System Configuration files use **gomplate templating** to reference operator configuration: - Use `{{ .cloud.domain }}` for the operator's domain - Use `{{ .apps.appname.configKey }}` for app-specific configuration - Use `{{ .operator.email }}` for operator email - All template variables must be defined in either the app's `manifest.yaml` under `defaultConfig` or be standard Wild Cloud operator variables Templates are compiled when users add apps to their Wild Cloud instance via the web app, CLI, or API. ### Label Strategy Wild Cloud uses a consistent labeling approach powered by Kustomize's `includeSelectors: true` feature: ```yaml labels: - includeSelectors: true pairs: app: appname # App name (matches directory) managedBy: kustomize partOf: wild-cloud ``` This automatically applies labels to all resources AND their selectors. Individual resources can use simple component-specific selectors like `component: web` or `component: worker`, and Kustomize will expand them to include the standard Wild Cloud labels. **Important:** Do NOT use Helm-style labels (`app.kubernetes.io/name`, `app.kubernetes.io/instance`). Use simple component labels instead. ## Working with Apps ### Creating/Modifying Apps 1. **Manifest fields:** - `name` - Must match directory name - `description` - Brief app description - `version` - App version (follow upstream versioning) - `icon` - URL to app icon - `requires` - List of dependency apps (e.g., `postgres`, `redis`, `memcached`) - `defaultConfig` - Default configuration values (will be added to operator's `config.yaml`) - `requiredSecrets` - List of secrets in dotted path format (e.g., `apps.appname.dbPassword`) 2. **Kustomization requirements:** - Must include standard Wild Cloud labels with `includeSelectors: true` - Namespace must match app name - List all resource files under `resources:` 3. **Security contexts:** All pods must comply with Pod Security Standards: ```yaml securityContext: runAsNonRoot: true runAsUser: 999 # Use appropriate non-root UID runAsGroup: 999 seccompProfile: type: RuntimeDefault containers: - securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: false # Set to true when possible ``` ### Secrets Management Secrets use **full dotted paths** as keys: ```yaml env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: appname-secrets key: apps.appname.dbPassword # Full path, not just "dbPassword" ``` **Database URL secrets:** When apps need database URLs with embedded credentials, always use a dedicated `dbUrl` secret in `requiredSecrets`. Do NOT try to construct URLs with env var substitution in templates - Kustomize cannot process runtime environment variables. ### Database Initialization Jobs Apps requiring PostgreSQL/MySQL databases should include a `db-init-job.yaml`: - Creates the app database if it doesn't exist - Creates/updates the app user with proper password - Grants appropriate permissions - For PostgreSQL jobs: use `runAsUser: 999` (postgres user) - Include any required database extensions (e.g., Immich requires `vector`, `cube`, `earthdistance`) Examples: [immich/db-init-job.yaml](immich/db-init-job.yaml), [gitea/db-init-job.yaml](gitea/db-init-job.yaml) ### External DNS Configuration Ingress resources should include external-dns annotations: ```yaml annotations: external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }} external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" ``` This creates CNAME records pointing app subdomains to the main cluster domain. ### Converting Helm Charts Wild Cloud prefers Kustomize over Helm for transparency and Git-friendliness. To convert a Helm chart: ```bash helm fetch --untar --untardir charts repo/chart-name helm template --output-dir base --namespace namespace --values values.yaml release-name charts/chart-name cd base/chart-name kustomize create --autodetect ``` Then: 1. Add `manifest.yaml` 2. Replace hardcoded values with gomplate variables 3. Update secrets to use Wild Cloud's dotted-path approach 4. Replace Helm labels with simple component labels 5. Add standard Wild Cloud labels to `kustomization.yaml` 6. Add security contexts to all pods ## Managing Wild Cloud Apps Users can manage apps through: 1. **Web App**: Navigate to the Apps page in your instance to browse, add, configure, and deploy apps 2. **CLI**: Use the Wild CLI for terminal-based workflows: - `wild app list` - List all available apps - `wild app add ` - Add an app (compiles templates, updates config/secrets) - `wild app deploy ` - Deploy an app to the cluster - `wild app list-deployed` - List deployed apps - `wild app status ` - Get app status - `wild app delete ` - Delete an app 3. **API**: Use the Wild Central API endpoints for automation: - `GET /api/v1/apps/available` - List all available apps - `GET /api/v1/apps/available/{app-name}` - Get app details - `POST /api/v1/instances/{instance-name}/apps` - Add an app (compiles templates, updates config/secrets) - `POST /api/v1/instances/{instance-name}/apps/{app-name}/deploy` - Deploy an app to the cluster - `GET /api/v1/instances/{instance-name}/apps` - List deployed apps - `GET /api/v1/instances/{instance-name}/apps/{app-name}/status` - Get app status ## Validation Before submitting changes: 1. Ensure `manifest.yaml` has all required fields 2. Verify `kustomization.yaml` includes standard Wild Cloud labels 3. Check all templates use valid configuration paths defined in `defaultConfig` 4. Confirm secrets use full dotted-path keys 5. Verify all pods have proper security contexts 6. Test template compilation works with sample operator config ## Examples of Well-Structured Apps - **immich** - Multi-deployment app with database init, multiple services, PostgreSQL extensions - **openproject** - App with multiple dependencies (postgres, memcached), configmap-based configuration - **gitea** - Standard app with database init, PVC for storage