9.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
buun-stack is a Kubernetes development stack for self-hosted environments with enterprise-grade components (k3s, Vault, Keycloak, PostgreSQL, Longhorn) orchestrated through Just task runner recipes.
Essential Commands
Development Setup
mise install # Install all required tools
just env::setup # Interactive environment configuration
just # Show all available commands
Just Task Runner Usage
- Module Structure: Justfiles are organized by modules (e.g.,
just keycloak::admin-password) - List All Recipes: Run
justto display all available recipes across modules - Module-Specific Help: Run
just <module>(e.g.,just keycloak) to show recipes for that module - Execution Location: ALWAYS run all recipes from the top directory (buun-stack root)
Core Installation Sequence
just k8s::install # Deploy k3s cluster
just longhorn::install # Storage layer
just vault::install # Secrets management
just postgres::install # Database cluster
just keycloak::install # Identity provider
just keycloak::create-realm # Initialize realm
just vault::setup-oidc-auth # Configure Vault OIDC
just k8s::setup-oidc-auth # Enable k8s OIDC auth
Common Operations
# User management
just keycloak::create-user # Interactive user creation
just keycloak::add-user-to-group <user> <group>
# Secret management
just vault::put <path> <key>=<value> # Store secret (OIDC auth)
just vault::get <path> <field> # Retrieve secret
# Database
just postgres::create-db <name> # Create database
just postgres::psql # PostgreSQL shell
# Testing/validation
kubectl --context <host>-oidc get nodes # Test OIDC auth
Architecture & Key Patterns
Module Organization
- Justfiles: Each module has its own justfile with focused recipes
- TypeScript Scripts:
/keycloak/scripts/contains Keycloak Admin API automation - Templates:
*.gomplate.yamlfiles use environment variables from.env.local - Custom Extensions:
custom.justcan be created for additional workflows
Authentication Flow
- Keycloak provides OIDC identity for all services
- Vault uses Keycloak for authentication via OIDC
- Kubernetes API server validates tokens against Keycloak
- All OIDC users automatically get cluster-admin role
Environment Variables
The .env.local file (created by just env::setup) contains critical configuration:
LOCAL_K8S_HOST: Internal SSH hostnameEXTERNAL_K8S_HOST: External FQDN for k8s APIKEYCLOAK_HOST: Keycloak FQDNVAULT_HOST: Vault FQDNKEYCLOAK_REALM: Realm name (default: buunstack)
TypeScript Utilities
All scripts in /keycloak/scripts/ follow this pattern:
- Use
@keycloak/keycloak-admin-clientfor API operations - Validate environment with
tiny-invariant - Load config from
.env.localusing@dotenvx/dotenvx - Execute with
tsxruntime
Credential Storage Pattern
The credential storage approach depends on whether External Secrets Operator is available:
When External Secrets is available (determined by helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE}):
- Credentials are generated and stored in Vault using
just vault::putcommands - Vault commands are used for secret management
# Example: PostgreSQL superuser password (only when External Secrets is available)
just vault::get secret/postgres/superuser password
When External Secrets is NOT available:
- Credentials are stored directly as Kubernetes Secrets
- Vault commands are NOT used
Secret Management Rules
-
Environment File: Do NOT write to
.env.localdirectly for secrets. Use it only for configuration values. -
Vault and External Secrets Integration:
-
When Vault and External Secrets are available, ALWAYS:
- Store secrets in Vault
- Create ExternalSecret resources to sync secrets from Vault to Kubernetes
- Let External Secrets Operator create the actual Secret resources
-
Check availability with:
if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then # Use Vault + External Secrets pattern fi
-
-
Fallback Pattern: Only create Kubernetes Secrets directly when Vault/External Secrets are not available.
-
Helm Values Secret References:
- When Helm charts support referencing external Secrets (via
existingSecret,secretName, etc.), ALWAYS use this pattern - Create the Secret using External Secrets (preferred) or directly as Kubernetes Secret
- Reference the Secret in Helm values instead of embedding credentials
- When Helm charts support referencing external Secrets (via
-
Keycloak Client Configuration:
- Prefer creating Public clients (without client secret) when possible
- Public clients are suitable for browser-based applications and native apps
- Only use confidential clients (with secret) when required by the service
-
Password Generation:
- Use
just utils::random-passwordwhenever possible to generate random passwords - Avoid using
openssl rand -base64 32or other direct methods - This ensures consistent password generation across all modules
- Use
Important Considerations
-
Root Token: Vault root token is required for initial setup.
-
OIDC Configuration: When creating services that need authentication:
- Create Keycloak client with
just keycloak::create-client - Configure service to use
https://${KEYCLOAK_HOST}/realms/${KEYCLOAK_REALM}
- Create Keycloak client with
-
Cloudflare Tunnel: Required hostnames must be configured with "no TLS verify" for self-signed certificates:
ssh.domain→ SSH localhost:22vault.domain→ HTTPS localhost:443auth.domain→ HTTPS localhost:443k8s.domain→ HTTPS localhost:6443
-
Helm Values: All Helm charts use gomplate templates for dynamic configuration based on environment variables.
-
Cleanup Operations: Most modules provide cleanup recipes (e.g.,
just keycloak::delete-user) with confirmation prompts. -
Trino and Lakekeeper Integration: When setting up Trino with Lakekeeper (Iceberg REST Catalog):
- The Keycloak client MUST have service accounts enabled for OAuth2 client credentials flow
- The
lakekeeperclient scope MUST be added to the Trino client - An audience mapper MUST be configured to set
aud: lakekeeperin JWT tokens - Trino REQUIRES
fs.native-s3.enabled=trueto handles3://URIs, regardless of vended credentials - When
vended-credentials-enabled=false, static S3 credentials must be provided via environment variables - All these configurations are automatically applied by
just trino::installwhen MinIO storage is enabled
Testing and Validation
After setup, validate the stack:
# Test Kubernetes OIDC auth
kubectl --context <host>-oidc get nodes
# Test Vault OIDC auth
vault login -method=oidc
vault kv get secret/test
# Check service health
kubectl get pods -A
Development Workflow
When adding new services:
- Create module directory with justfile
- Add gomplate templates for Helm values if needed
- Store credentials in Vault using established patterns
- Create Keycloak client if authentication required
- Import module in main justfile
Helm Chart Installation Guidelines
-
Helm Values Modification:
- MANDATORY: Read the complete official values.yaml file BEFORE making any changes
- MANDATORY: Check template files to understand how configuration values are used
- MANDATORY: Look for existing working examples in the official documentation
- MANDATORY: Test each configuration change incrementally, not all at once
- When external database integration is needed, search for "external", "existing", "secret" patterns in values.yaml
- Never assume configuration structure - always verify against official sources
- If unsure about a configuration, ask the user to provide official documentation links
-
Debugging Approach:
- When Helm deployments fail, ALWAYS check the generated Secret/ConfigMap contents first
- Compare expected vs actual configuration values using kubectl describe/get
- Check pod logs and environment variables to understand what the application is actually receiving
- Test database connectivity separately before assuming chart configuration issues
-
Resource Creation Consistency:
- When creating Secret/ExternalSecret/ConfigMap resources, follow patterns from existing modules
- Maintain consistent naming conventions and label structures
- Use the same YAML formatting and organization as other modules
-
Core Component Protection:
- Keycloak, PostgreSQL, and Vault are core components
- NEVER restart or reinstall these components without explicit user approval
- These services are critical to the entire stack's operation
Code Style
- Indent lines with 4 spaces
- Do not use trailing whitespace
- It must pass the command:
just --fmt --check --unstable - Follow existing Justfile patterns
- Only write code comments when necessary, as the code should be self-explanatory (Avoid trivial comment for each code block)
- Write output messages and code comments in English