# AGENTS.md This file provides guidance for agents working in the Darano monorepo. ## Repository Structure Multi-service monorepo for the Darano financial/crypto platform. Each subdirectory is its own independent git repository: | Directory | Language | Role | |-----------|----------|------| | `api/` | Go (Gin) | HTTP REST gateway — the only public-facing service | | `auth/` | Go (gRPC) | Authorization — OTP, JWT, permissions, identity | | `wallet/` | Go (gRPC) | Wallet — assets, transactions, Stellar blockchain, market | | `ui/` | TypeScript/Next.js | Customer-facing frontend | | `AdminPanel/` | Python/Django | Internal admin panel (bypasses api, hits Postgres directly) | | `proto/` | Protobuf | Central schema definitions shared by all services | | `DevOps/` | Docker Compose | Infrastructure — Postgres, Redis, RabbitMQ, MinIO, Traefik | | `docs/` | MkDocs | Documentation site | **Root-level**: `Procfile` (deployment), `CLAUDE.md` (Claude Code config) — these are the monorepo's coordination files, not service-level. ## Service Communication Architecture ``` Browser/Client → api (REST/HTTP) → auth, wallet/market/alert (gRPC) AdminPanel ──────────────────────→ Postgres directly (bypasses api) ``` The `api` gateway is the **only** HTTP-facing service. All inter-service communication is gRPC. ### API Gateway (api/) - **Entry/composition**: `api/main.go` → `cmd.Execute()` (Cobra) → `cmd/apiRuntime`, which explicitly owns clients, handlers, router, HTTP servers, permission synchronization, and cleanup. - **Upstream boundary**: `api/application/port.Upstreams` composes generated service contracts. `api/infrastructure/grpcclient` implements lazy connection creation, active-call tracking, configurable idle closure (**2-minute default**), reconnection, health checks, and explicit shutdown without reflection. - **Handlers**: `api/interface/http/handler/` — one file per domain. Handlers depend on `application/port.Upstreams`. - **Routing**: `api/interface/http/handler/routing.go` — routes grouped into `/v1/public/`, `/v1/client/`, `/v1/admin/`. The admin group is protected but currently empty; internal routes remain disabled. - **Middleware chain** (order matters): APM → Profiling → Prometheus → CORS → JSON → I18n → Error → optional Logger - **Endpoints**: `GET /` (health), `GET /metrics` (Prometheus), `GET /swagger/*any`, `GET /ws` (WebSocket) - **Profiling**: `net/http/pprof` is imported (blank import `_`); runs on a separate port when `Profiling.Enabled` in config ### Wallet Binary (wallet/) Single binary hosts multiple sub-services via Cobra subcommands: `wallet`, `market`, `alert`, `internal_wallet`, `stream`. These are spawned concurrently via `errgroup` in `cmd/cmdServe/main.go`. **Concurrent build safety**: The Makefile uses `flock` on `./.build/air-build.lock` to serialize protobuf generation and binary writes when multiple air instances run simultaneously (`make dev-wallet`, `make dev-market`, etc. all trigger `make build`). ### Config - **Go services** use `knadh/koanf` (not `fig` as older docs may suggest) to parse TOML config files - **Global singleton**: `config.Cfg` — a `sync.Once` ensures it's initialized exactly once - **Defaults baked into code**: `config.go` in `api/` sets default `ipg_callback_url` and `ui_server_error_status_url` before loading the TOML file (TOML overrides defaults) - **Struct tags**: use `koanf:"field-name"` (not env vars) ### Config files reference | Service | Config file | |---------|------------| | API | `./cfg.toml` (or `--conf ./config.cfg`) | | Wallet sub-services | `./wallet.cfg.toml`, `./market.cfg.toml`, `./alert.cfg.toml`, `./stream.cfg.toml` | | Internal wallet | `./wallet.internal.cfg.toml` | ## Commands ### Go services (`api/`, `auth/`) ```bash cd api/ # or cd auth/ make dep # install buf, protoc plugins, swag, air make build # fmt + proto gen + binary → ./build/main make dev # build + hot reload via air make test # go test ./... ./build/main serve --conf ./cfg.toml ``` ### Wallet service (`wallet/`) ```bash cd wallet/ make build # proto gen + binary make dev-wallet # hot reload wallet sub-service (.wallet.air.toml) make dev-market # hot reload market sub-service (.market.air.toml) make dev-stream # hot reload stream sub-service (.stream.air.toml) make run-wallet # ./build/main serve wallet -c ./wallet.cfg.toml make run-market # ./build/main serve market -c ./market.cfg.toml make run-internal # ./build/main serve internal_wallet -c ./wallet.internal.cfg.toml make run-alert # ./build/main serve alert -c ./alert.cfg.toml make run-stream # ./build/main stream -c ./stream.cfg.toml make grpc-ui-wallet # open grpcui on port 7210 (localhost:8200) make grpc-ui-market # open grpcui on port 7300 (localhost:8300) make grpc-ui-alert # open grpcui on port 7400 (127.0.0.1:8400) ``` **Important**: When running multiple sub-services concurrently via `make run` or multiple `make dev-*` instances, the Makefile uses `flock` to prevent concurrent protobuf generation or binary overwrites. Don't parallelize make (`-j`) without `flock` — it will corrupt stub files. ### AdminPanel (`AdminPanel/`) ```bash cd AdminPanel/ # DB setup (uv virtual env) uv run python src/manage.py makemigrations uv run python src/manage.py migrate make run # runserver on 0.0.0.0:8080 make dev # watch mode using funzzy make proto # buf generate → proto stubs via betterproto make build-msg # compile Django messages (i18n) ``` ### UI (`ui/`) ```bash cd ui/ yarn build-proto # generate TS types from protos (buf generate) yarn dev # Next.js dev server (also runs buf generate) make dev # same, bound to 0.0.0.0:3000 yarn lint # ESLint yarn lint:fix # ESLint with auto-fix yarn build # production build (also runs buf generate) ``` ## Protobuf Code Generation All services depend on generated code. **Always run `make build-proto` (or `yarn build-proto` for UI) before building any service.** Proto definitions live in `proto/` with subdirectories: `base/`, `auth/`, `wallet/`, `market/`, `alert/`, `errors/`. Each service has its own `buf.gen.yaml` controlling code generation output into `domain/stub/go/` (Go) or `src/types/stub/` (TypeScript). **Gotcha**: The Go Makefiles **strip `omitempty`** from all JSON struct tags in `.pb.go` files after generation. This is intentional — protobuf JSON serialization needs consistent field presence. Do not re-add `omitempty` manually. ## Go Service Layer Pattern Both `auth` and `wallet` follow a layered architecture: - **`config/`** — TOML config via koanf. `config.Cfg` global singleton, `sync.Once` initialized. - **`domain/stub/go/`** — Generated protobuf Go code. **Never edit manually.** - **`cmd/`** — Cobra CLI entry points. Subcommands map to serve modes. - **`repository/`** — Data access. Aggregates `IPostgres`, `IRedis`, `IService`, and `IQueue` (wallet only) interfaces. - **`core/`** (wallet) / **`usecase/`** (auth) — Business logic. Implements gRPC server interfaces from proto. - **`util/`** — Shared helpers; no business logic. In `auth`, `usecase.UseCase` interface directly embeds the generated gRPC server interfaces (`authv1.AuthorizationServiceServer`, `authv1.InternalAuthorizationServiceServer`). In `wallet`, `core/` contains sub-packages: `walletImp/`, `marketImp/`, `alertImp/`, `cronJobs/`. ## API Gateway Patterns ### Request/Response Flow ``` HTTP request → interface/http middleware → interface/http/handler → application Upstreams port → infrastructure/grpcclient → backend service ``` - **Response helpers**: `interface/http/handler/response.go` — `JSON()` and `JSONList[X]()` wrap responses in `transport.Response{Meta, Data}`. Pointer-backed empty lists use the `EmptyList` sentinel to serialize `[]` rather than `null`. - **HTTP-to-gRPC context**: `interface/http/handler/contextWithMetadata()` copies HTTP headers into incoming and outgoing gRPC metadata. - **Handler interface**: `interface/http/handler.Server` receives the `application/port.Upstreams` boundary and configuration explicitly. ### Route conventions - Public routes: `/v1/public/{domain}/{action}` — no JWT needed - Client routes: `/v1/client/{domain}/{action}` — JWT required (validated by `AuthorizationMiddleware`) - Admin routes: `/v1/admin/{domain}/{action}` — JWT required (mostly placeholder currently) ## UI Architecture - **Next.js App Router** under `src/app/`. Notable route groups: `dashboard/` (auth-protected), `auth/login/`, `blog/` (MDX content), `projects/`, `receipt/`. - **Auth**: `src/middleware.ts` uses next-auth to protect `/dashboard/:path*`. Exports default from `next-auth/middleware`. - **Services**: `src/services/` — plain functions wrapping axios calls to the api gateway, typed with generated protobuf types. - **State**: `src/stores/` — Zustand stores. - **Hooks**: `src/hooks/` — TanStack Query hooks on top of service functions. - **Components**: HeroUI + Tailwind CSS with RTL support (Persian/Farsi UI). - **Forms**: Formik + Yup (older), react-hook-form (newer). - **Date picker**: `@amir04lm26/react-modern-calendar-date-picker` (Jalali calendar). - **MDX**: Blog content uses `.mdx` files with remark/rehype plugins. - **Path alias**: `@/*` maps to `./src/*`. - **Output**: `standalone` mode for Docker. Image domains hardcoded in `next.config.mjs` remote patterns. ## Infrastructure Dev infrastructure: `DevOps/dev/compose.yml` — PostgreSQL 16, Redis, RabbitMQ, MinIO. Each Go service has its own DB name. All Go services instrumented with Elastic APM and Prometheus metrics. Traefik handles TLS/routing in production. ## Gotchas and Non-Obvious Details 1. **Config library**: Services use `knadh/koanf` with TOML parser, not `fig`. Tags are `koanf:"field-name"`. 2. **gRPC connection lifecycle**: Connections are lazy — created on first RPC. Active calls prevent idle closure; peers close after the configured inactivity timeout (2-minute default) and reconnect on the next call. 3. **Peer mapping**: `infrastructure/grpcclient` maps configured peers explicitly; the former reflection/enum generator is removed. 4. **Swagger docs**: Generated by `swag init` and served via `interface/http`. URL adapts per environment (prod → `api.darano.ir`, dev → `dev.api.darano.ir`, local → `localhost:`). 5. **Multi-service wallet**: The wallet binary serves 5 sub-services. `make run` kills existing processes matching the service name pattern via `pgrep | xargs kill` before starting. 6. **Concurrent dev**: Running multiple `make dev-*` targets simultaneously requires the `flock` serialization in the Makefile. Using `make -j` without flock will corrupt the build. 7. **Proto stubs excluded from watchers**: `.air.toml` excludes `domain/stub/` from file watching. Air also excludes `swagger/` and `testdata/`. 8. **API testing requests**: `api/req/` contains JS files for API request testing (axios-based) — not part of the app, useful for manual testing. 9. **Django Admin**: Uses `unfold` theme (AdminPanel admin customizations in `sites.py` and `unfoldconf.py`). 10. **Proto generation for AdminPanel**: Generates into root directories (`base/`, `wallet/`, etc.) then deletes them with `rm -rf`. The betterproto generator outputs Python classes directly. ## AdminPanel Deep Dive - **Architecture**: Django admin panel that bypasses all Go backend services. Connects directly to `core_db` (same Postgres as Go services) for read/write. Uses Django DB routers in `src/adminpanel/db/routers.py` to route `coreLogic` models to `core_db` and other apps to `default`. - **Models**: `src/coreLogic/models.py` contains `managed = False` Django models — manual replicas of Go GORM models generated via `inspectdb`. **Not auto-generated from protos.** `make proto` generates betterproto Python stubs separately but Django models are maintained by hand. **Never edit models.py manually** — it drifts from Go services. - **Admin classes**: `src/coreLogic/admin/` — 17 admin files (`asset.py`, `wallets.py`, `market.py`, etc.). All inherit from `MultiDBModelAdmin` in `src/utils/base_admin.py` which handles: multi-database writes (`using="core_db"`), soft deletes via `deleted_at`, asset-level permission filtering, and Jalali date widgets. - **Permission system**: `src/usermapper/user_perm.py` + `src/coreLogic/acl.py` — admin users get asset-level access control. `save_model` checks `user_perm.can_access_asset()` before allowing writes. - **Key gotchas**: - `check_token_policy()` in `admin/asset.py:161` duplicates validation from wallet service. Changes to asset validation must be made in **both** places. - `GENERIC_ASSET_META_VALUE` in `admin/asset.py:31` is a hardcoded JSON blob — if the wallet service changes asset metadata structure, this must be updated too. - The router has a typo: `no_migartion` → should be `no_migration`. - All `coreLogic` models are read via `core_db` directly. **Any admin write bypasses Go services** — no trustline updates, no blockchain operations, no validation from wallet/market services. ## Ongoing Refactoring: DDD / Clean Architecture This repository is being migrated to a unified Domain-Driven Design / Clean Architecture. See [`REFACTORING-PLAN.md`](REFACTORING-PLAN.md) for the full plan. **Go services current state**: API, Auth, and Wallet configuration and architecture phases are complete on `feat/refactor-v1`; use the refactoring tracker and audit for the current package boundaries and verification evidence. **AdminPanel current state**: Direct DB access to `core_db` with no gRPC layer. Business logic duplicated in Django admin classes (`check_token_policy`, `auto_gen` instead of delegating to Go services). `managed = False` models drift from Go GORM models. **Target**: All Go services follow `domain/` → `application/` → `infrastructure/` → `interface/` with inward dependencies. AdminPanel routes writes through gRPC to Go services while keeping reads from DB for performance. **Migration phases** (see plan for details): Config standardization → auth/ restructure → wallet/ restructure → api/ restructure → AdminPanel service layer → shared domain types. Parallel workstreams: Go services restructure can run alongside AdminPanel gRPC client creation.