This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build System (Makefile-driven):
make dev # Clean build from scratch: proto + build
make build # Build all binaries (server, agent) to /bin
make proto # Generate gRPC code using Buf (buf generate)
make pack # Generate pack artifacts via protoc-gen-croupier
make test # Run unit tests with race detection
make clean # Remove build artifacts and generated codeLocal Development Setup:
git clone --recursive https://github.com/cuihairu/croupier.git
go mod download && make submodules
./scripts/dev-certs.sh # Generate self-signed TLS certs
buf lint && buf generate # Generate proto code
make build # Build binariesTesting:
make test # All tests with race detection
go test ./internal/... # Subset testing
./bin/croupier-server --config configs/server.yaml validate # Config validationCode Style:
gofmt -w . # Format all Go files
gofmt -l . # List files that need formattingIMPORTANT: Before committing any changes, ALWAYS run gofmt -w . to ensure all Go files are properly formatted.
Croupier implements a three-tier distributed GM backend system:
- Permission Control Layer - RBAC/ABAC system independent of game logic
- Game Control Layer - Function registration-driven game operations
- Observable Display Layer - Descriptor-driven UI generation
Server (internal/server/)
- Central control plane with gRPC (8443) + HTTP REST (18780)
- Two main services:
ControlService(agent registration) andFunctionService(invocation routing) - Features: load balancing, RBAC, audit chain, approval workflows, multi-game scoping
Agent (internal/agent/)
- Distributed proxy in game networks, outbound mTLS to Server
- Local gRPC listener (19090) for game server function registration
- Bidirectional tunnel support for request/response multiplexing
- Job execution with async streaming, idempotency, cancellation
Web UI → Server (HTTP) → Load Balancer → Agent → Game Server
Protocol-First Development:
- All APIs defined in
proto/using Buf toolchain - Custom protoc plugin (
protoc-gen-croupier) generates pack artifacts - Generated code in
gen/(ignored in git)
CRITICAL: Protobuf Code Generation
- ALWAYS use
make prototo generate protobuf code - Buf uses remote plugins with fixed versions (e.g., buf.build/protocolbuffers/go:v1.36.11)
- NEVER use local
protocdirectly - it will generate incompatible code - Local protoc version mismatches will cause compilation failures
- Buf config (
buf.gen.yaml) specifies exact plugin versions to match CI environment
Descriptor-Driven Architecture:
- Functions defined via protobuf + JSON Schema descriptors
- UI auto-generates forms, validation, and permission checks from single source
- Function packs (
.tgz) bundle descriptors, schemas, and UI plugins
Configuration Management:
- Multi-layer: YAML → includes → profiles → env vars → CLI flags
- Environment prefixes:
CROUPIER_SERVER_*,CROUPIER_AGENT_* - Config validation:
./croupier config test
Idempotency & Task Model:
- All operations support
idempotency-keyto prevent duplicate side effects - Async tasks with event streaming (progress/logs/done/error)
- Task cancellation via
CancelTaskRPC
Build Tags for Features:
pgtag: PostgreSQL support for approvalssqlitetag: SQLite approvals store- Enables flexible deployment options
cmd/ # Binary entry points (server, agent, unified CLI)
proto/ # Protobuf definitions (Buf workspace)
internal/server/ # Server business logic (control, function, http, registry)
internal/agent/ # Agent logic (tunnel, local server, jobs)
internal/auth/ # RBAC, JWT, TOTP, user management
internal/function/ # Descriptor loading and validation
internal/jobs/ # Job state machine and execution
internal/loadbalancer/ # Load balancing strategies (RR, consistent hash, least conn)
sdks/ # Multi-language SDKs (submodules: go, cpp, java)
web/ # Frontend submodule (Umi Max + Ant Design)
configs/ # Configuration templates and examples
examples/ # Demo game servers and invokers
Security Architecture:
- Enforced mTLS for all inter-service communication
- Field-level masking for sensitive data in audit logs
- Two-person rule enforcement for high-risk operations
- Audit chain with hash-based integrity
Multi-Game Scoping:
- All operations scoped by
game_id/envfor tenant isolation - Registry indexed by
(game_id, function_id)for function routing - HTTP headers
X-Game-ID/X-Envpropagated through call chain
Load Balancing Abstraction:
- Strategy interface with multiple implementations
- Health checking integrated with agent selection
- Supports routing modes: lb, broadcast, targeted, hash
Unit tests focus on:
- RBAC policy grant/deny logic (
internal/auth/rbac/) - Job executor state transitions and idempotency (
internal/agent/jobs/) - Sensitive field masking (
internal/server/http/) - Pack import/export workflows
- Registry agent session management
Integration examples in examples/ demonstrate end-to-end flows from function registration through UI invocation.
All HTTP APIs must follow one response contract. Do not mix envelope and non-envelope styles.
- Use standard HTTP status code (
200/201/204). - Return business payload directly as JSON object/array.
- Do not wrap success payload in
{ "code": ..., "message": ..., "data": ... }.
Examples:
200 OK+{ "id": 1, "name": "admin" }200 OK+{ "items": [...], "total": 10 }204 No Contentwith empty body
- Use standard HTTP error status (
400/401/403/404/409/422/500). - Body uses unified error object:
error: stable machine-readable code (snake_case)message: human-readable messagedetails(optional): structured validation/business detail
Example:
401 Unauthorized+{ "error": "unauthorized", "message": "未授权" }
HTTP status and response body have different responsibilities. Do not duplicate them.
- HTTP status expresses the error class:
400: invalid request / validation error401: unauthenticated / token invalid403: authenticated but forbidden404: resource not found409: conflict422: semantically invalid but well-formed request500: internal server error
- Response body expresses the readable and structured error detail:
error: stable code for frontend branchingmessage: user-facing error textdetails: optional structured payload for form fields or extra diagnostics
Recommended examples:
400 Bad Request
{
"error": "validation_failed",
"message": "请求参数无效",
"details": {
"gameId": "不能为空"
}
}401 Unauthorized
{
"error": "unauthorized",
"message": "未授权"
}409 Conflict
{
"error": "conflict",
"message": "资源状态冲突"
}Frontend rules:
- Route and global handling should primarily branch on HTTP status.
- UI text should come from
message. - Stable client logic should branch on
error, not on localizedmessage. - Form-level rendering should read
detailswhen present. - Do not require frontend to read a business
codefield when HTTP status already exists.
- SSE endpoints must return
text/event-stream(not JSON envelope). - Health/readiness probes may return minimal payload (for example
{ "status": "ok" }). - File/binary download endpoints follow content-type requirements instead of JSON.
- Prefer
internal/common/responsefor API handlers. - Do not introduce new handlers based on
internal/pkg2/responseenvelope style. - Middleware auth failures must keep the same JSON error shape as above.
- New APIs must keep response shape consistent with existing REST handlers in
internal/api.
Before merge, scan for direct ad-hoc response writes:
rg -n "\bc\.(JSON|IndentedJSON|String|Data|PureJSON|XML|YAML|ProtoBuf)\(" internal --glob "!**/*_test.go"Any new direct write must be justified as an exception (SSE/health/binary), otherwise refactor to unified response helpers.
Configuration naming must be standardized. Do not invent new casing per file or per module.
This repository previously drifted into mixed styles such as Server vs storage, Driver vs driver, Dir vs dir, Enabled vs enabled. That is invalid going forward.
- YAML keys:
lowerCamelCase - JSON keys:
lowerCamelCase - Go struct fields:
PascalCase - Environment variables:
UPPER_SNAKE_CASE - CLI flags:
kebab-case
Examples:
server:
host: 0.0.0.0
port: 18780
timeout: 600000
mode: dev
database:
driver: mysql
dataSource: xxx
storage:
driver: file
baseDir: data/uploads
cache:
enabled: true
type: redis- New config structs must use
yaml:"lowerCamelCase"andjson:"lowerCamelCase". - Do not add new config tags using
Server,Driver,Dir,Enabled,BaseDirstyle keys. - Do not mix uppercase and lowercase siblings in the same config object.
- Config examples under
configs/*.yamlmust use canonical keys only. - If old configs already exist, backward compatibility must be handled in code, not by continuing to write new examples in legacy style.
- Legacy config keys may be tolerated only in compatibility parsing code.
- Compatibility exists only to avoid breaking old deployments.
- Compatibility code must not become the new documentation standard.
- All docs, examples, generated templates, and new tests must use canonical
lowerCamelCase.
Before merge, check for mixed or legacy config naming:
rg -n 'yaml:"[A-Z]' internal/config
rg -n '^\s+[A-Z][A-Za-z0-9]*:' configs/*.yaml configs/**/*.yamlIf a change introduces new uppercase YAML keys, it is a review failure unless the file is explicitly a legacy compatibility fixture.