Ctrl K
ComunidadGuías…

CLAUDE.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Lucity

Open-source PaaS on Kubernetes with full ejectability. Monorepo with a single Go control-plane binary plus a Vue 3 dashboard.

## Project

- **Go workspace**: `go.work` with multi-module layout (Go 1.27)
- **Module path**: `github.com/zeitlos/lucity`
- **Monorepo**: `services/conductor` (control plane), `services/stagehand` (workload changes nobody asked for: plan-based placement), `services/cashier` (usage metering), `services/exporter` (external usage metrics), `services/dashboard` (Vue), `services/switchboard` (chat apps for agents), `pkg/` (shared Go), `charts/` (Helm)
- **Platform images**: `ghcr.io/zeitlos/lucity/{conductor,stagehand,cashier,exporter,dashboard,docs,switchboard}`
- **User workload images**: Zot (self-hosted OCI registry, `localhost:5000` in dev)
- **Coding rules**: see `.claude/rules/` for architecture, Go, frontend, GraphQL, marketing, and working conventions

## Build & Run

- **Conductor**: `go run ./cmd/conductor/...` from `services/conductor/`, or `make dev-conductor`
- **Cashier**: `go run ./cmd/cashier/...` from `services/cashier/`, or `make dev-cashier`
- **Stagehand**: `go run ./cmd/stagehand/...` from `services/stagehand/`
- **Exporter**: `go run ./cmd/exporter/...` from `services/exporter/` (needs the `OVH_*` env vars)
- **Dashboard**: `npm run dev` from `services/dashboard/`
- **Build all**: `make build`
- **GraphQL codegen**: `go generate ./internal/api/graphql/resolver.go` from `services/conductor/`

## Architecture

### Stateless Design

The platform has no central database. All state is derived from external systems:

- **Kubernetes**: namespaces, labels, Helm release state (Secrets), operator CRDs
- **OCI Registry (Zot)**: built images, tags, digests
- **Identity Provider (OIDC/Logto)**: users, roles, authentication, workspace metadata

The platform is non-intrusive — its downtime does not affect running workloads.

### Single-Repository Model

- **User's source repo** (GitHub): read-only to the platform, never written to
- **No GitOps repo**: the conductor deploys workloads as standard Helm releases applied imperatively (Helm SDK). Deployment config is the chart values it computes; Helm stores the release state in-cluster.

### Multi-Tenant (Workspaces)

Each Lucity instance supports multiple workspaces. A workspace is the tenant boundary — it owns projects, environments, and members. Workspace context is derived from the JWT token (workspace claim). Kubernetes namespaces carry `lucity.dev/workspace` labels for discovery.

### Services

| Service | Port | Protocol | Purpose |
|---------|------|----------|---------|
| Conductor | 8080 (HTTP), 9004 (webhook HTTP), 9008 (metrics) | HTTP | Unified control plane: GraphQL, Helm release management for user changes, build orchestration, GitHub webhook receiver, the Stripe billing a user triggers (trial, plan selection, checkout, portal). Runs no background loops |
| Stagehand | 9010 (metrics) | Kubernetes + Stripe (read) + Logto | Every background loop: build/deploy admission (event-driven), namespace scaffolding, chart upgrades, plan-based placement (dry-run capable), billing + abuse suspension (computed every pass, no webhooks), custom-domain verification + edge sync, backup recovery floors |
| Cashier | 9009 (metrics) | Stripe + VictoriaMetrics + Kubernetes (read) | Usage metering into Stripe meter events, monthly plan credits |
| Exporter | 9007 (HTTP) | Prometheus `/metrics` | Usage from external providers (object storage sizes) as metrics, scraped by the node-local otel agent |
| Dashboard | 5173 | HTTP | Vue 3 SPA |
| Switchboard | none, outbound only | Telegram Bot API + Mattermost + runner API | Chat apps of every workspace: Telegram and Mattermost bots bridged to agent runners, approvals in the chat. Reads chat apps and agent channels from its own namespace |

### Conductor internal layout

```
services/conductor/
├── cmd/conductor/                  # main, config, OIDC login, GraphQL + webhook servers
├── internal/
│   ├── api/
│   │   ├── graphql/                # gqlgen schema, resolvers, directives, models
│   │   └── webhook/                # GitHub webhook receiver (github/, http/)
│   ├── conductor/                  # Client facade tying the domain packages together
│   ├── buildjob/                   # build Job orchestration (kubernetes/)
│   ├── planner/                    # source detection + build planning (railpack/)
│   ├── source/                     # user source repo access (github/)
│   ├── directory/                  # user/workspace directory (logto/)
│   ├── dbquery/                    # database explorer query execution
│   └── billing/                    # Stripe billing a user triggers (trial, plans, checkout, portal)
```

### Communication

- **Dashboard ↔ Conductor**: GraphQL over HTTP (port 8080)
- **No service calls another**: conductor, stagehand and cashier share state through Stripe, Logto and the Helm releases only
- **GitHub → Conductor**: HTTP webhooks (port 9004)
- **Long-running operations**: polling (watch registry for images, poll Helm/Kubernetes for rollout status)

### Shared Packages (`pkg/`)

graceful (server lifecycle), logger (slog + tint), deployer (Helm release apply + lucity-app values, shared by conductor and stagehand), platform (IDs + value types; kubernetes/ = discovery from Kubernetes), environment (namespace lifecycle), jobs + pipeline (build/deploy Job cache and admission), hostname (custom-domain DNS verification), edge (Bunny CDN), domains (custom-domain attach + edge sync), resources, image, usererror, billing (Stripe subscription lookup, plans, standing), auth (OIDC/JWT), labels (K8s label constants), tenant (workspace context), github (App + OAuth), logto (Logto Management API), to (pointer/conversion helpers).

## Feature Development Workflow

1. **Research** — how do Railway, Heroku, Render, Fly.io handle this?
2. **Architecture fit** — does it respect stateless design? Is it ejectable?
3. **Day-2 operations cost** — can a small team run it?
4. **Design APIs** — GraphQL schema
5. **Design deployment values** — how does this affect the lucity-app chart values?
6. **Design frontend** — Vue pages, composables, GraphQL queries
7. **Implement minimal** — ship the smallest useful version first
8. **Test** — GraphQL playground, dashboard end-to-end, Go unit tests
9. **Iterate** — extend with more advanced capabilities

## Smoke Testing

```sh
make dev          # conductor + cashier + dashboard with hot reload
```

Then:

- GraphQL playground: http://localhost:8080/playground
- Dashboard: http://localhost:5173/
- Webhook receiver: POST to http://localhost:9004/webhooks/github

Logs land in `tmp/logs/{conductor,cashier,dashboard}.log`.

## Known Issues

_None yet — this is a fresh scaffold._