Files
dev-procfile/AGENTS.md
T

222 lines
14 KiB
Markdown

# 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:<port>`).
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.