From zero to a running stack, plus what to flip for production
Everything below is checked against the real Makefile, deploy/
compose files, cmd/ binaries and internal/core/config/config.go —
including a handful of env var name mismatches between docs and code that are worth
knowing about before you lose an hour to them.
1.23+ (module declares go 1.23.0, toolchain go1.24.4) — both backend Dockerfiles currently build from golang:1.21-alpine, a real mismatch worth fixing before relying on make build
PostgreSQL
15 (docker-compose provides it; for local-only runs, any 13+ works)
Redis
7
Node.js
18+ (admin console), npm
Flutter
stable channel, iOS/Android toolchains as needed
Docker + Docker Compose
for the containerized path
Quick start & make targets
local, without Docker
# backend — needs Postgres + Redis reachable per .env
make backend-run # cd mynex-backend && go run ./cmd/server# admin console
make admin-dev # cd mynex-admin && npm run dev# mobile app
cd mynex_mobile && flutter run --dart-define=API_BASE_URL=http://localhost:8181/api
# or: ./scripts/run_flutter.sh
Target
Does
make backend-run
cd mynex-backend && go run ./cmd/server — the real entrypoint, not go run main.go
make backend-test
go build ./... && go vet ./... && go test ./...
make admin-dev / admin-build
Vite dev server / vue-tsc -b && vite build (type-check + production build)
make mobile-analyze / mobile-test
flutter analyze / flutter test
make test
All of the above in sequence — the full local CI gate
make dev-install
go mod download + npm install + flutter pub get
make fmt / lint
go fmt + dart format / go vet
make build / up / down / restart / logs
Production docker-compose lifecycle (deploy/docker-compose.yml)
make dev / dev-up / dev-down / dev-logs
Development docker-compose lifecycle (deploy/docker-compose.dev.yml) — hot reload via air
make db-backup / db-restore FILE=...
pg_dump / psql against the running mynex-postgres container
mynex-admin/package.json has no test or lint script
CLAUDE.md's old generic guidance mentions npm run test / npm run lint
for the admin console — neither exists in the real package.json (only
dev, build, preview), and no test framework is even a
dependency. Type-checking happens as part of npm run build (vue-tsc -b).
Docker Compose
deploy/docker-compose.yml (prod). Dev compose additionally exposes the API directly on 8181 + a delve port 2345, runs admin via npm run dev rather than a built image, and adds adminer/redis-commander/mailhog for local inspection.
deploy/nginx/nginx.conf routes /api → the API (prefix stripped), /ws → the API with WebSocket upgrade headers + 7-day timeouts, everything else → the admin SPA with 404→index.html fallback. Rate-limit zones are pre-configured (10r/s general, 5r/m on login) but the HTTPS server block is commented out — TLS certs aren't wired by default.
Discrepancy: both compose files set CORS_ORIGINS on the API container, but config.go reads ALLOWED_ORIGINS — the compose-set value is silently ignored and the app falls back to its own default.
Sandbox mode
Confirmed accurate: the platform is fully runnable with zero external keys.
Two switches control everything:
PAYMENTS_PROVIDER = sandbox (default)
Top-ups settle deterministically after PAYMENTS_SANDBOX_SETTLE_DELAY (5s). No Stripe/bank keys required. Setting STRIPE_SECRET_KEY registers the real Stripe provider alongside sandbox; per-kind overrides (PAYMENTS_PROVIDER_CARD etc.) let you mix real and sandbox per payment method.
CHAIN_ENV = sandbox (default)
NAQD mints/burns/transfers against an in-memory chain — deterministic, idempotent per reference. CHAIN_ENV=live hard-fails at boot (not a silent fallback) if SOLANA_WALLET_PRIVATE_KEY+NAQD_TOKEN_MINT_ADDRESS or POLYGON_WALLET_PRIVATE_KEY+NAQD_ERC20_CONTRACT_ADDRESS aren't set.
Cards (CardProcessor) and partner rails (PartnerRail) currently have only a sandbox implementation each — there's no env var to flip them to "real" yet, because no production implementation has been written.
Oracle already works off CoinGecko alone without it — this just adds a second real source for the median
Real card processor
— not yet buildable via config
No production CardProcessor implementation exists. A real BIN sponsor integration needs to be written against the interface first
Real TnG rail
— not yet buildable via config
No production PartnerRail implementation exists. Same — write one against the interface, register via Service.RegisterRail
Full environment variable reference
Consolidated from internal/core/config/config.go, the four docs/wiring/*.md
files, and direct os.Getenv grep across every package. Vars marked
ignored
are set somewhere in this repo's own docker-compose/.env.example files but read under a
different name by the actual Go code — setting the documented name does nothing.
Core / server
Var
Default
Notes
APP_ENV
development
Server refuses to boot in production with default JWT secrets
APP_PORT
:8181
mynex-backend/.env.example says :8080 and both Dockerfiles EXPOSE 8080 — all stale; the real default, both compose files, and the README agree on 8181
ALLOWED_ORIGINS
http://localhost:3000
docker-compose sets ignoredCORS_ORIGINS instead — has no effect
Only Africa's Talking has a real implementation; Twilio/Nexmo return "not implemented"; unset → mock
FCM_SERVER_KEY, APNS_*
Real FCM client and mocked APNS client exist but PushService is never constructed anywhere — dead code today
Docker-compose-only (not read by the Go app)
POSTGRES_DB/USER/PASSWORD/PORT, NGINX_HTTP_PORT/HTTPS_PORT, VITE_API_BASE_URL, VITE_WS_URL (build-args for the admin image), and the ignored CORS_ORIGINS.
Two unrelated .env.example files exist
Root /my_nex/.env.example is docker-compose-shaped (POSTGRES_*,
NGINX_*, VITE_*) and doesn't match config.go's actual
variable names at all. mynex-backend/.env.example is the one that matches
config.go — but even it has the S3_BUCKET/APPLE_PAY_MERCHANT_ID/
APP_PORT=:8080 issues noted above. When in doubt, trust
internal/core/config/config.go over any .env.example file.
Seeds & tools
Binary
Does
Respects .env?
go run ./cmd/migrate migrate
Manually triggers the same AutoMigrate that already runs on every server boot — the migrations/ directory it might imply is empty; there's no separate up/down migration framework
yes
go run ./cmd/tools/seed_admin
Creates [email protected] / AdminPass123!, role super_admin. Skips if already present
Compares every wallet's cached balance against its ledger sum; non-zero exit on any mismatch — CI/cron-friendly
yes
Three tools won't work against a docker-compose database as-is
create_demo_user, create_test_user, and
seed_exchange_rates hardcode two different, mutually inconsistent Postgres
credential sets directly in source, neither of which matches the docker-compose defaults
(mynex/mynex_secret) or mynex-backend/.env.example's
default (mynex/password). Edit the DSN in the source file directly
if you need these against a non-default local database — seed_admin and
reconcile are the two that behave normally.
Automatic startup seeds (no manual step needed)
Ledger system accounts — bootstrapped for MVR/USD/NAQD on every boot (BootstrapSystemAccounts).
FX defaults — USD/MVR and NAQD/USD seeded on every boot (fx.Service.SeedDefaults); once the NAQD oracle is live, NAQD/USD reads are actually served by the oracle-chained rate provider instead, and this seed becomes a pure fallback.
Partner rails — tng-my config row seeded idempotently on every boot (SeedDefaultRails), immediately followed by a one-shot RecoverStuckOperations sweep.