Adds complete media storage pipeline with GCS presigned uploads, AI image/video/text generation via queue-based workers, realtime SSE event streaming, and comprehensive skeleton packages (storage, mediagen, textgen, generation, realtime, persona, routing, ai-client). Includes security fixes for media delete authorization, nil pointer guards in handlers, video persistence via download-then-upload, consistent signed URLs, and Image→ImageIcon rename to avoid DOM collision. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.3 KiB
Citadel Integration Plan
rdev integrates with a partner-hosted Citadel instance at citadel-staging.orchard9.ai for centralized log aggregation, querying, and alerting across both the platform and generated projects.
Environment Architecture
Organization: orchard9 (on citadel-staging.orchard9.ai)
│
├── Environment: "rdev-platform"
│ ├── rdev-api logs (stdout via agent)
│ ├── rdev-worker logs (stdout via agent)
│ ├── claudebox logs (stdout via agent)
│ └── audit events (shipped from AuditLogger)
│
├── Environment: "<project-slug>" (auto-created per project)
│ └── Project pod logs (stdout via agent, routed by k8s labels)
│
├── Tenant Group: "platform" → rdev-platform
└── Tenant Group: "projects" → all project-* environments
Each project gets its own Citadel environment, matching rdev's isolation model (each project gets its own DB, Redis, DNS, registry namespace).
Integration Points
1. Agent DaemonSet (Log Collection)
What: A citadel-agent DaemonSet on every k3s node collects container stdout/stderr and ships to Citadel via HTTPS.
Why: rdev already outputs structured JSON slog to stdout. Zero code changes needed.
Routing: Agent reads k8s labels to route logs to the correct Citadel environment:
citadel.io/environmentlabel → determines target environmentcitadel.io/servicelabel → tags the service name in Citadel
Manifests: deployments/k8s/base/citadel-agent/
2. Citadel Client Adapter (API Integration)
What: Go HTTP client for Citadel's API, following rdev's hexagonal architecture (port interface + adapter).
Why: Needed for auto-provisioning environments and shipping audit logs.
Files:
internal/port/citadel.go— Port interfaceinternal/adapter/citadel/client.go— HTTP client implementationinternal/adapter/citadel/audit_shipper.go— Audit log shipping
3. Project Provisioning Step
What: When ProjectInfraService.CreateProject() runs, a new step creates a Citadel environment for the project.
Why: Each project needs its own isolated log environment. Auto-provisioning eliminates manual setup.
Where in the flow:
CreateProject()
1. Generate slug
2. Create project in DB
3. Create git repo
4. Create DNS
5. Activate CI
6. Seed template
7. Provision DB + cache
8. *** Create Citadel environment *** ← NEW
9. Create initial deployment
10. Trigger CI build
Rollback: On project deletion, the Citadel environment is deleted too.
4. Skeleton Template Labels
What: Add citadel.io/* labels to the skeleton's k8s deployment templates.
Why: The agent uses these labels to route project logs to the correct Citadel environment.
Template vars: {{CITADEL_TENANT_ID}} injected during provisioning.
5. Audit Log Shipping
What: Hook into rdev's existing AuditLogger to also ship audit events to Citadel.
Why: Unified search across application logs and security events.
Pattern: Wrap the existing PostgreSQL AuditLogger with a multi-writer that sends to both PostgreSQL and Citadel.
Configuration
Environment Variables (rdev-api)
| Variable | Description | Example |
|---|---|---|
CITADEL_URL |
Partner Citadel instance URL | https://citadel-staging.orchard9.ai |
CITADEL_API_KEY |
API key for environment management | ck_live_... |
CITADEL_TENANT_ID |
Platform environment tenant ID | uuid |
CITADEL_ENABLED |
Enable/disable Citadel integration | true |
Secrets (k8s)
# deployments/k8s/base/citadel-agent/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: citadel-agent
namespace: observability
type: Opaque
stringData:
api-key: "ck_live_..."
tenant-id: "..."
Queries (What You Get)
Platform Operations
# All errors across platform
citadel query "level:error" --tenant rdev-platform --last 1h
# Track a project lifecycle
citadel query "project_id:my-project" --tenant rdev-platform --last 24h
# Build failures
citadel query "component:build level:error" --tenant rdev-platform
# Saga failures
citadel query "component:saga result:failure" --tenant rdev-platform
# Slow API requests
citadel query "duration_ms:>5000 component:http" --tenant rdev-platform
# Audit trail
citadel query "action:project.create" --tenant rdev-platform --last 7d
Generated Projects
# Query a specific project's logs
citadel query "level:error" --tenant <project-slug> --last 1h
# All project errors at once
citadel query "level:error" --group projects --last 1h
# Correlate platform + project
citadel query "project_id:my-project" --last 24h # org-wide
Implementation Order
| Phase | Task | Effort |
|---|---|---|
| 1 | Write Citadel client adapter (internal/adapter/citadel/) |
2h |
| 1 | Write port interface (internal/port/citadel.go) |
30m |
| 2 | Deploy agent DaemonSet (deployments/k8s/base/citadel-agent/) |
1h |
| 2 | Add citadel labels to existing rdev-api/worker manifests | 15m |
| 3 | Add Citadel env creation to ProjectInfraService.CreateProject() |
2h |
| 3 | Add Citadel env deletion to ProjectInfraService.DeleteProject() |
30m |
| 3 | Add migration for citadel_tenant_id column on projects table |
15m |
| 4 | Add citadel.io/* labels to skeleton k8s templates |
30m |
| 4 | Add CITADEL_TENANT_ID to template variables |
15m |
| 5 | Implement audit log shipper | 2h |
| 5 | Wire into main.go startup | 30m |
Files Created/Modified
New Files
internal/port/citadel.gointernal/adapter/citadel/client.gointernal/adapter/citadel/audit_shipper.godeployments/k8s/base/citadel-agent/kustomization.yamldeployments/k8s/base/citadel-agent/daemonset.yamldeployments/k8s/base/citadel-agent/serviceaccount.yamldeployments/k8s/base/citadel-agent/configmap.yamlinternal/db/migrations/024_citadel_tenant_id.sql
Modified Files
deployments/k8s/base/kustomization.yaml— include citadel-agentdeployments/k8s/base/rdev-api.yaml— add citadel labels + env varsinternal/service/project_infra.go— add CitadelClient fieldinternal/service/project_infra_crud.go— add provisioning + cleanup stepsinternal/adapter/templates/templates/skeleton/— k8s template labelscmd/rdev-api/main.go— wire Citadel adapter