Files
dev-procfile/AGENTS.md
T

14 KiB

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.gocmd.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/)

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/)

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/)

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/)

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.goJSON() 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 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.