Getting Started
Prerequisites
| Tool |
Version |
Needed for |
| Node.js |
20.x (both Dockerfiles use node:20-alpine) |
backend, admin portal |
| npm |
ships with Node 20 |
backend (package-lock.json), portal |
| Bun |
1.3 (oven/bun:1.3-alpine in the Dockerfile) |
website build |
| Docker + Compose |
v2 syntax (prod runs Docker 29 / Compose v5) |
local data stores, prod stack |
| Flutter / Dart |
Dart SDK ≥ 3.3 (aimgold_app/pubspec.yaml) |
mobile app only |
| Go |
1.25 (aim-gold-backend/go.mod) |
only the parked Go rewrite |
Install and run locally
Backend (customer API + admin API)
cd aim-digigold-real
docker compose up -d # local postgres:16, redis:7, minio on default ports
npm ci
# create .env.development — see the configuration table below; the minimum is
# DB_*, REDIS_HOST, JWT_SECRET, FIELD_ENCRYPTION_KEY (64 hex), SAFEGOLD_BASE_URL
npm run start:api # :3000, Swagger at http://localhost:3000/docs
npm run start:admin-api # :3001, Swagger at http://localhost:3001/docs
Both apps load .env.<NODE_ENV> then .env
(apps/*/src/app.module.ts). Useful scripts: npm run lint:ci, npm test,
npm run migration:generate -- libs/database/src/migrations/<Name>,
npm run migration:run (TS, local) / npm run migration:run:prod (compiled).
Seed an admin user for the portal / admin API:
npm run seed:admin # prompts for email/password (or ADMIN_EMAIL/ADMIN_PASSWORD env)
Website
cd aimgold-website
bun install
bun run dev # http://localhost:5199
bun run typecheck && bun run build
Admin portal
cd digigold-admin
npm ci
# .env.local: NEXTAUTH_URL=http://localhost:3002, NEXTAUTH_SECRET=<random>,
# ADMIN_API_INTERNAL_URL=http://localhost:3001
npm run dev # http://localhost:3002
Mobile app
cd aimgold_app
flutter pub get
flutter run --flavor development -t lib/main_development.dart
Production
Production is deploy-by-push: see Architecture → Deploy flow.
The stack lives on the VPS under /opt/aim/deploy; deploy/gen-env.sh
generates a fresh .env.production on a new box, and deploy/README.md is
the ops runbook (rollback, seeding, backups, migrations).
Configuration reference
Every environment variable read by the deployed system, by component.
(The parked Go rewrite documents its own surface in
aim-gold-backend/.env.example.)
Secrets
Never commit values. Production values live only in
/opt/aim/deploy/.env.production, /opt/aim/deploy/portal.env.production
and /opt/aim/monitoring/.env on the server.
Backend (aim-digigold-real — validated by libs/config/src/validation.schema.ts)
| Variable |
Purpose |
Required |
Example |
NODE_ENV |
environment selector; picks .env.<NODE_ENV> |
no (default development) |
production |
API_PORT |
customer API port |
no (default 3000) |
3000 |
ADMIN_API_PORT |
admin API port |
no (default 3001) |
3001 |
API_PUBLIC_URL |
public origin for absolute URLs (image proxy links) |
no (default https://api.aimgold.org) |
https://api.example.org |
DB_HOST |
Postgres host (compose overrides to digigold_postgres) |
yes |
localhost |
DB_PORT |
Postgres port |
no (default 5432) |
5432 |
DB_USER |
Postgres user (also POSTGRES_USER in compose + backup step) |
yes |
digigold |
DB_PASSWORD |
Postgres password |
yes |
<random> |
DB_NAME |
database name |
yes |
digigold |
DB_SYNCHRONIZE |
TypeORM auto-sync — must stay false; schema is migration-managed |
no (default false) |
false |
DB_LOGGING |
TypeORM query logging |
no (default false) |
false |
REDIS_HOST |
Redis host (compose overrides to digigold_redis) |
yes |
localhost |
REDIS_PORT |
Redis port |
no (default 6379) |
6379 |
REDIS_PASSWORD |
Redis auth (also feeds --requirepass in compose) |
prod yes |
<random> |
JWT_SECRET |
HMAC secret for all JWTs |
yes (min 16 chars) |
<64-hex> |
JWT_ACCESS_TTL |
customer access-token TTL |
no (default 15m) |
15m |
JWT_REFRESH_TTL |
customer refresh-token TTL |
no (default 30d) |
30d |
ADMIN_JWT_ACCESS_TTL |
admin token TTL (matches 8 h portal session) |
no (default 8h) |
8h |
FIELD_ENCRYPTION_KEY |
AES key for PII fields — unrecoverable if lost |
yes (exactly 64 hex) |
<64-hex> |
DEV_FIXED_OTP |
dev-only: forces every OTP to this value — never set in prod |
no |
000000 |
SAFEGOLD_BASE_URL |
SafeGold partner API base |
yes (uri) |
https://partners-staging.safegold.com |
SAFEGOLD_API_KEY |
SafeGold bearer key (env-only; required in production) |
prod yes |
<key> |
SAFEGOLD_GOLD_TOKEN |
SafeGold gold-product token (env-only; required in production) |
prod yes |
<token> |
SAFEGOLD_ENCRYPTED |
legacy AES-envelope wire format toggle |
no (default false) |
false |
SAFEGOLD_PARTNER_ID |
SafeGold partner id |
no |
<id> |
RAZORPAY_KEY_ID / RAZORPAY_KEY_SECRET |
Razorpay payments keys (payments stubbed until set) |
no |
rzp_live_… |
RAZORPAY_WEBHOOK_SECRET |
payment webhook HMAC secret |
no |
<secret> |
RAZORPAY_X_KEY_ID / RAZORPAY_X_KEY_SECRET / RAZORPAY_X_ACCOUNT |
RazorpayX payout keys + source account |
no |
<…> |
RAZORPAY_X_WEBHOOK_SECRET |
payout webhook HMAC secret |
no |
<secret> |
KYC_PROVIDER / KYC_API_KEY / KYC_WEBHOOK_SECRET |
KYC provider wiring (stubbed until set) |
no |
<…> |
S3_ENDPOINT |
S3/MinIO endpoint (compose overrides to http://digigold_minio:9000) |
no |
http://localhost:9000 |
S3_ACCESS_KEY / S3_SECRET_KEY |
S3 creds (also MinIO root creds in compose) |
prod yes |
<random> |
S3_BUCKET |
app bucket (auto-created by minio_init) |
prod yes |
digigold |
SMS_USERNAME / SMS_APIKEY |
alots.in SMS gateway creds (env-only; required in production; OTP sends are live once set) |
prod yes |
<…> |
SMS_SENDER / SMS_TEMPLATE_ID / SMS_ROUTE / SMS_BASE_URL |
SMS sender id, DLT template, route, endpoint |
no (defaults in code) |
AIMECO |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS |
outbound email (stubbed until set) |
no |
smtp.example.com |
ADMIN_PORTAL_ORIGIN |
CORS allowlist for admin API — unset = closed (no cross-origin) |
recommended |
https://admin.example.org |
GITHUB_PAT |
server-side PAT powering the portal's Build Tools (503 without it; not in gen-env.sh — add manually) |
Build Tools only |
ghp_… |
GITHUB_REPO |
repo the Build Tools target |
no (default aimgoldorg/Karat) |
org/repo |
ENV_FILE |
dotenv override for TypeORM CLI + seed CLIs |
no |
.env.production |
ADMIN_EMAIL / ADMIN_NAME / ADMIN_PASSWORD / ADMIN_ROLE |
seed:admin CLI inputs (prompted if unset) |
no |
— |
Deploy layer (deploy/docker-compose.yml, values in server .env.production)
| Variable |
Purpose |
Required |
Example |
API_IMAGE_TAG |
GHCR tag for the backend image — pin sha-… for rollback |
no (default latest) |
sha-a1b2c3d |
WEBSITE_IMAGE_TAG |
GHCR tag for the website image |
no (default latest) |
sha-a1b2c3d |
ADMIN_PORTAL_IMAGE_TAG |
GHCR tag for the portal image |
no (default latest) |
sha-a1b2c3d |
Admin portal (portal.env.production on the server; NEXT_PUBLIC_* baked at image build)
| Variable |
Purpose |
Required |
Example |
NEXTAUTH_URL |
portal canonical URL |
yes at runtime |
https://admin.example.org |
NEXTAUTH_SECRET |
NextAuth session secret |
yes at runtime |
<random 32B> |
ADMIN_API_INTERNAL_URL |
admin API base for proxy/login (compose sets container URL) |
no (default http://localhost:3001) |
http://digigold_admin_api:3001 |
NEXT_PUBLIC_API_WS_URL |
customer-API origin for the price ticker (build-time; Dockerfile bakes prod value) |
no |
https://api.example.org |
NEXT_PUBLIC_BRAND_NAME |
brand label (build-time) |
no (default DigiGold) |
MyBrand |
NEXT_PUBLIC_MONITORING_URL |
Monitoring link target (build-time) |
no (default https://monitor.aimgold.org) |
https://monitor.example.org |
PORT / HOSTNAME / NODE_ENV / NEXT_TELEMETRY_DISABLED |
standalone server bind + mode (set in Dockerfile) |
no |
3002 / 0.0.0.0 |
Website (build-time only)
| Variable |
Purpose |
Required |
Example |
VITE_API_BASE_URL |
customer API origin baked into the bundle (src/lib/api.ts); Dockerfile doesn't set it, so images use the code default https://api.aimgold.org |
no |
https://api.example.org |
Monitoring (/opt/aim/monitoring/.env on the server)
| Variable |
Purpose |
Required |
Example |
GRAFANA_ADMIN_USER |
Grafana admin login |
no (default admin) |
admin |
GRAFANA_ADMIN_PASSWORD |
Grafana admin password — compose refuses to start without it |
yes |
<strong> |
CI (GitHub repo secrets)
| Secret |
Purpose |
VPS_HOST / VPS_USER / VPS_SSH_KEY |
SSH target + key for deploys and pinned health checks |
GHCR_PULL_USER / GHCR_PULL_TOKEN |
read:packages credentials the VPS uses to pull images |
GITHUB_TOKEN |
auto-provided; pushes built images to GHCR |
First task: run the login → price → portfolio flow locally
- Start the data stores and both APIs as above, with
DEV_FIXED_OTP=000000 in .env.development.
- Request an OTP (no SMS is sent when the fixed OTP is set):
curl -X POST localhost:3000/v1/auth/send-otp \
-H 'Content-Type: application/json' -d '{"phone":"+919999999999"}'
- Verify it — this auto-creates the user and returns tokens:
curl -X POST localhost:3000/v1/auth/verify-otp \
-H 'Content-Type: application/json' \
-d '{"phone":"+919999999999","otp":"000000","deviceId":"dev-1"}'
- Use the
accessToken:
curl localhost:3000/v1/price/current
curl -H "Authorization: Bearer $TOKEN" localhost:3000/v1/portfolio/summary
- Explore the rest interactively via Swagger at
localhost:3000/docs and
localhost:3001/docs, or read the API Reference.