Files
wild-directory/CLAUDE.md
2025-10-18 18:57:35 +00:00

158 lines
6.0 KiB
Markdown

# 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 by `wild-app-add` when operators add apps to their Wild Cloud home directory.
### 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
## Common Wild Cloud Commands
These commands are run by operators from their Wild Cloud home directory (not this repository):
- `wild-apps-list` - List all available apps
- `wild-app-add <app-name>` - Add an app (compiles templates, updates config/secrets)
- `wild-app-deploy <app-name>` - Deploy an app to the cluster
## 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