Backend Development Context
May 3, 2026 · View on GitHub
Part of CLAUDE.md Critical Conventions
When to load: Working on Go backend API, handlers, or Kubernetes integration
Quick Reference
- Language: Go 1.21+
- Framework: Gin (HTTP router)
- K8s Client: client-go + dynamic client
- Primary Files:
components/backend/handlers/*.go,components/backend/types/*.go
Critical Rules
Authentication & Authorization
ALWAYS use user-scoped clients for API operations:
reqK8s, reqDyn := GetK8sClientsForRequest(c)
if reqK8s == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"})
c.Abort()
return
}
FORBIDDEN: Using backend service account (DynamicClient, K8sClient) for user-initiated operations
Backend service account ONLY for:
- Writing CRs after validation (handlers/sessions.go:417)
- Minting tokens/secrets for runners (handlers/sessions.go:449)
- Cross-namespace operations backend is authorized for
Token Security
NEVER log tokens:
// ❌ BAD
log.Printf("Token: %s", token)
// ✅ GOOD
log.Printf("Processing request with token (len=%d)", len(token))
Token redaction in logs: See server/server.go:22-34 for custom formatter
Error Handling
Pattern for handler errors:
// Resource not found
if errors.IsNotFound(err) {
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
// Generic error
if err != nil {
log.Printf("Failed to create session %s in project %s: %v", name, project, err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create session"})
return
}
Type-Safe Unstructured Access
FORBIDDEN: Direct type assertions
// ❌ BAD - will panic if type is wrong
spec := obj.Object["spec"].(map[string]interface{})
REQUIRED: Use unstructured helpers
// ✅ GOOD
spec, found, err := unstructured.NestedMap(obj.Object, "spec")
if !found || err != nil {
return fmt.Errorf("spec not found")
}
Common Tasks
Adding a New API Endpoint
- Define route:
routes.gowith middleware chain - Create handler:
handlers/[resource].go - Validate project context: Use
ValidateProjectContext()middleware - Get user clients:
GetK8sClientsForRequest(c) - Perform operation: Use
reqDynfor K8s resources - Return response: Structured JSON with appropriate status code
Adding a New Custom Resource Field
- Update CRD:
components/manifests/base/[resource]-crd.yaml - Update types:
components/backend/types/[resource].go - Update handlers: Extract/validate new field in handlers
- Update operator: Handle new field in reconciliation
- Test: Create sample CR with new field
OOTB Workflows
Workflows live in github.com/ambient-code/workflows. Each has .ambient/ambient.json with: name, description, systemPrompt, startupPrompt, greeting.
greeting= user-facing text displayed instantly with typewriter effect (no LLM call)startupPrompt= instruction to Claude (reserved for future use, not currently sent)- Backend caches OOTB workflows for 5 min (
ootbCacheTTL) — restart backend to force refresh - Backend parses
ambient.jsonvia GitHub API — invalid JSON silently fails (returns empty fields)
Pre-Commit Checklist
- All user operations use
GetK8sClientsForRequest - No tokens in logs
- Errors logged with context
- Type-safe unstructured access
-
gofmt -w .applied -
go vet ./...passes -
golangci-lint runpasses
Key Files
handlers/sessions.go- AgenticSession lifecycle (3906 lines)handlers/middleware.go- Auth, RBAC validationhandlers/helpers.go- Utility functions (StringPtr, BoolPtr)types/session.go- Type definitionsserver/server.go- Server setup, token redaction