# 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: direct SQL reads, authenticated service-owned writes | | `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 ──reads───────────────→ Postgres AdminPanel ──authenticated gRPC──→ wallet (asset administration) ``` 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 - **Explicit ownership**: commands load configuration once and inject it through composition; legacy `config.Cfg` globals were removed - **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 The active Go services follow inward-facing layers: - **`domain/`** — entities, exact value objects, errors, and ports without transport/framework ownership. - **`application/`** — business policies and orchestration against domain ports. - **`infrastructure/`** — koanf configuration, PostgreSQL/Redis, external clients, queues, Stellar, and generated-client adapters. - **`interface/`** — gRPC/HTTP/process adapters and protocol mapping. - **`domain/stub/go/`** — Generated protobuf Go code. **Never edit manually.** - **`cmd/`** — Cobra CLI entry points. Subcommands map to serve modes. - **`repository/` / `usecase/`** — remaining compatibility composition/interfaces; implementations live in infrastructure and new business logic belongs in application/domain. In `auth`, `usecase.UseCase` interface directly embeds the generated gRPC server interfaces (`authv1.AuthorizationServiceServer`, `authv1.InternalAuthorizationServiceServer`). In `wallet`, runtime adapters live under `interface/grpc` and `interface/process`; the superseded `core/*Imp` packages no longer exist. ## 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**: `make proto` uses the adjacent local `proto/` checkout and generates only the active Base/Auth/Wallet BetterProto message subset into `src/stub/`. ## AdminPanel Deep Dive - **Architecture**: Django admin panel that reads `core_db` directly for fast projections. Unmanaged models are read-only by default; the Assets admin sends authenticated typed commands to Wallet instead of writing the database. Django DB routers route `coreLogic` reads 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/` — admin files inherit from `MultiDBModelAdmin` in `src/utils/base_admin.py`, which routes reads to `core_db`, applies asset-level filters/Jalali widgets, and makes unmanaged projections fail closed for add/change/delete. Assets opt into service-backed mutations only. - **Permission system**: `src/usermapper/user_perm.py` + `src/coreLogic/acl.py` — admin users get asset-level access control. Asset service commands enforce the same access decision before dispatch. - **Key gotchas**: - Asset policy and default metadata are owned by Wallet's `application/adminasset`; do not recreate them in Django. - The Wallet admin client requires matching AdminPanel `WALLET_ADMIN_GRPC_TOKEN` and internal-wallet `[admin-assets].token` configuration. - All `coreLogic` models are read via `core_db` directly. Adding a mutation requires an explicit typed owning-service workflow; never opt an unmanaged model into direct ORM writes. ## 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 reads remain, while unmanaged projections fail closed for writes. Asset upsert/deactivation uses an authenticated Wallet gRPC adapter; duplicated asset policy and metadata generation have been removed from Django. `managed = False` models can still drift from Go persistence models and must remain projection-only. **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.