# CLAUDE.md > **Cross-repo rules** — see `/Users/wen/project/rag/CLAUDE.md` for full workspace conventions. > - .NET backends share JWT key: `RagJwtSecretKey2026MustBeAtLeast32CharsLong!` > - gRPC auth: call `rag-backend:50051` for token validation + permission checks > - Other repos: `rag-backend` (5211), `im-system` (5212), `work-flow`, `rag-frontend` (5666 Vue) ## Build & Run ```bash go run ./cmd/server -conf configs/config.yaml # HTTP :8080, gRPC :9000 make api # Regenerate proto code make wire # Regenerate Wire DI go test ./... # All tests buf generate # Regenerate proto docker compose up -d # Via local build ``` ## Architecture Go 1.25 microservice. Kratos framework + DDD four-layer + Watermill CQRS. ``` cmd/server/main.go → Entry point, load config cmd/server/wire.go → Wire DI declarations cmd/server/wire_gen.go → Wire generated code api/file/v1/ → Proto definitions (HTTP+gRPC dual protocol) internal/conf/ → Config structs (proto-defined) internal/biz/ → Business logic layer (repo interface definitions + usecases) internal/data/ → Data access layer (GORM + S3, implements biz repo interfaces) internal/service/ → Service implementation (implements proto Service interface) internal/server/ → HTTP/gRPC server creation and middleware internal/watermark/ → Watermill CQRS (CommandBus + EventBus) internal/pkg/sanitize/ → Input sanitization utilities internal/pkg/s3errors/ → S3 error mapping configs/config.yaml → Local dev config ``` ## Layered Call Chain ``` service (DTO conversion) → biz (business logic) → data (data access) ↕ watermark (CQRS commands/events) ``` ## Tech Stack - **Kratos** — HTTP + gRPC framework, proto-first API definition - **Wire** — Compile-time dependency injection - **Watermill** — CQRS (CommandBus + EventBus), PGSQL as message store - **GORM** — Business data ORM (folders, files, share_links) - **AWS SDK v2** — S3 interface to RustFS/MinIO - **PostgreSQL** — Business data + Watermill message queue ## Code Patterns ### Wire ProviderSet Pattern Each layer defines a ProviderSet: ```go // internal/data/data.go var ProviderSet = wire.NewSet(NewData, NewFileRepo, NewFolderRepo, NewFileMetaRepo, NewShareRepo) // internal/biz/biz.go var ProviderSet = wire.NewSet(NewFileUsecase, NewBucketUsecase, NewFolderUsecase, NewShareUsecase) // internal/service/service.go var ProviderSet = wire.NewSet(NewFileService) // internal/server/server.go var ProviderSet = wire.NewSet(NewHTTPServer, NewGRPCServer) ``` Wire bindings (in cmd/server/wire.go): - `biz.FileRepo` → `*data.FileRepo` - `biz.FolderRepo` → `*data.FolderRepo` - `biz.FileMetaRepo` → `*data.FileMetaRepo` - `biz.ShareRepo` → `*data.ShareRepo` ### GORM Transaction Management ```go // biz layer defines interface type Transaction interface { InTx(ctx context.Context, fn func(ctx context.Context) error) error } // data layer implements func (d *Data) DB(ctx context.Context) *gorm.DB { tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB) if ok { return tx } return d.db.WithContext(ctx) } ``` ### Proto Error Definition Errors defined in `api/file/v1/error_reason.proto`: ```protobuf enum ErrorReason { FILE_NOT_FOUND = 0 [(errors.code) = 404]; INVALID_PARAMETER = 1 [(errors.code) = 400]; } ``` Usage: `return nil, api.file.v1.ErrorFileNotFound("file %s not found", key)` ### GORM Model Naming - Models suffixed with `PO`: `FolderPO`, `FileMetaPO`, `ShareLinkPO` - Table names via `TableName()` method with snake_case ### Service → Biz → Data Pattern ```go // service: proto request → usecase call → proto response func (s *FileService) UploadFile(ctx context.Context, req *pb.UploadFileRequest) (*pb.UploadFileResponse, error) { err := s.fileUC.UploadFile(ctx, req.BucketName, req.ObjectKey, bytes.NewReader(req.Data)) return &pb.UploadFileResponse{Message: "uploaded"}, err } // biz: business logic, repo interface calls func (uc *FileUsecase) UploadFile(ctx context.Context, bucket, key string, data io.Reader) error { return uc.repo.UploadFile(ctx, bucket, key, data) } // data: concrete implementation func (r *FileRepo) UploadFile(ctx context.Context, bucket, key string, data io.Reader) error { _, err := r.client.PutObject(ctx, &s3.PutObjectInput{...}) return s3errors.Wrap(err) } ``` ### Configuration `configs/config.yaml` for local development. Environment variables via `${ENV_VAR}` placeholders. Loaded via Kratos config component with file source. ### Middleware Chain (Kratos) HTTP and gRPC share the same middleware: ``` recovery → tracing → logging ``` Configured in `internal/server/http.go` and `internal/server/grpc.go`.