Overview
The CBDC Issuer Console is a pnpm + Turborepo monorepo split into apps (runnable services) and packages (shared libraries). Dependencies flow in one direction: apps depend on packages, never the reverse.
cbdc-issuer/
├── apps/
│ ├── api/ NestJS REST API (TypeORM + PostgreSQL, CosmJS)
│ ├── chain/ Wrapper around the cbdc-node Cosmos chain (Go, git submodule)
│ ├── dashboard/ Vite + React 19 web console (TanStack Router/Query)
│ ├── docs/ Docusaurus (this site)
│ └── playground/ Scratch app for experiments
├── packages/
│ ├── shared/ Pure TS enums + types (Role, MultisigType, …)
│ ├── api-client/ Auto-generated OpenAPI client
│ ├── frontend/ React hooks layer (TanStack Query, Zustand, Inversify)
│ ├── design-system/web/ Tailwind v4 + React UI component library
│ └── backend/ core / email / cache / queue NestJS libraries
├── turbo.json
├── docker-compose.yml
└── pnpm-workspace.yaml
Note:
apps/chainis a thin wrapper (Makefile +init-chain.sh) around the canonical chain binarycbdcd, vendored as a git submodule atapps/chain/nodefrom the separatePeersyst/cbdc-noderepository. The chain's customx/cbdcmodule (mint/burn) lives in that repo, not here.cbdc-nodeis an Ethermint-based Cosmos SDK fork, so accounts useeth_secp256k1keys (coin type 60).
How the pieces connect
The API is the only service that talks to PostgreSQL and the blockchain. The Dashboard is a pure frontend — it calls the API for everything (in dev, via Vite's /api proxy to http://localhost:3001).
Tech stack at a glance
| Layer | Technology |
|---|---|
| Backend | NestJS 11, TypeORM, PostgreSQL 15, Passport JWT, CosmJS |
| Frontend | Vite, React 19, TanStack Router (file-based), TanStack Query, CASL |
| UI | @cbdc-issuer/design-system-web (React + Tailwind CSS v4), i18next |
| Chain | Cosmos SDK chain cbdc-node (binary cbdcd), custom x/cbdc + x/group |
| Monorepo | Turborepo, pnpm workspaces (workspace:*) |
| Console (dev) / AWS SES / Resend |
Key design decisions
| Decision | Why |
|---|---|
| Chain as source of truth | Groups, members, policies, proposals, votes, balances and supply live on-chain and are queried in real time. Postgres holds only off-chain data (users, org/token config, whitelist labels, address book, invitations, activity-log audit). |
| Chain-agnostic interfaces | The API talks to the chain through four injection tokens (CHAIN_GOVERNANCE, CHAIN_TOKEN, CHAIN_BROADCAST, TRANSACTION_BUILDER). A different chain means a new adapter, not new business logic. |
| Abstracted custody | Per-user secp256k1 keys are AES-256-GCM encrypted in the database with CUSTODY_SECRET. The signing path is isolated in the custody module, so an HSM/KMS swap stays contained. |
| Polling over WebSocket | Live data is refreshed via TanStack Query refetchInterval (~30s, with a 15s stale time). Simple and sufficient for governance workflows. |
| Single deployment, multi-org | One deployment per ecosystem. The Central bank and its Commercial banks are modelled as Organization records in-app, not via tenant isolation. |