Skip to main content

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/chain is a thin wrapper (Makefile + init-chain.sh) around the canonical chain binary cbdcd, vendored as a git submodule at apps/chain/node from the separate Peersyst/cbdc-node repository. The chain's custom x/cbdc module (mint/burn) lives in that repo, not here. cbdc-node is an Ethermint-based Cosmos SDK fork, so accounts use eth_secp256k1 keys (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

LayerTechnology
BackendNestJS 11, TypeORM, PostgreSQL 15, Passport JWT, CosmJS
FrontendVite, React 19, TanStack Router (file-based), TanStack Query, CASL
UI@cbdc-issuer/design-system-web (React + Tailwind CSS v4), i18next
ChainCosmos SDK chain cbdc-node (binary cbdcd), custom x/cbdc + x/group
MonorepoTurborepo, pnpm workspaces (workspace:*)
EmailConsole (dev) / AWS SES / Resend

Key design decisions

DecisionWhy
Chain as source of truthGroups, 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 interfacesThe 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 custodyPer-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 WebSocketLive data is refreshed via TanStack Query refetchInterval (~30s, with a 15s stale time). Simple and sufficient for governance workflows.
Single deployment, multi-orgOne deployment per ecosystem. The Central bank and its Commercial banks are modelled as Organization records in-app, not via tenant isolation.