The developer console for Mux Protocol — manage API keys, track wallet creation, and monitor account activity on Stellar.
Mux Dashboard is the interface for developers building on Mux. It provides visibility into the Invisible Wallet system while abstracting away all blockchain complexity.
Mux Dashboard allows developers to:
- Create and manage API keys for SDK access
- Track Stellar account creation on Testnet and Mainnet
- Monitor wallet activity and balances
- View usage metrics such as transaction counts and account status
- Configure basic project-level settings
End users do not interact with this dashboard — it is purely for developers integrating Mux into their applications.
- Developer-first UX: designed for fast onboarding and management
- Invisible Wallet visibility: see accounts and activity without exposing keys or blockchain jargon
- Safe and clear: all actions are explicit; sensitive operations are handled by the backend
- API Key Management: generate, rotate, and revoke keys
- Wallet/Account Tracking: monitor accounts created via the SDK
- Activity Metrics: view transaction volumes and status
- Requests over time: visualize API request traffic trends
- Wallet creation analytics: monitor daily wallet creation volume
- Network Switching: testnet vs mainnet tracking
- Usage Monitoring: see platform-sponsored actions and account health
- Node.js >= 18
- Access to Mux Backend API
git clone https://github.com/mux-labs/mux-frontend.git
cd mux-frontend
pnpm install
pnpm run devAll variables are optional in local development — sensible mock/default
behavior kicks in when they're unset (see src/lib/env.ts for the
validation schema). Copy .env.example to .env.local and fill in real
values for testnet/mainnet-connected work.
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_API_URL |
No | (none) | Base URL for the Mux backend API used by client-side requests, e.g. https://api.muxprotocol.com for mainnet or a testnet-specific URL. When unset, API routes such as /api/auth/login and /api/wallets fall back to an in-repo mock so pnpm run dev and CI work without a live backend — but only when NODE_ENV is not production (see the production note below). Set this in new deploys; use the aliases below only for backward compatibility. An alias set to an empty string (e.g. NEXT_PUBLIC_API_URL=) is treated as unset and the next alias in the chain is tried (see API_URL_CANDIDATES in src/lib/api/config.ts). |
NEXT_PUBLIC_MUX_API_URL |
No | https://api.muxprotocol.com |
Legacy alias for the API base URL, checked after NEXT_PUBLIC_API_URL (see src/lib/api/config.ts). Kept for backward compatibility with older deploys. |
NEXT_PUBLIC_API_BASE |
No | (none) | Third fallback in the API base URL resolution chain, checked after the two vars above. |
NEXT_PUBLIC_APP_URL |
No | http://localhost:3000 |
Public-facing URL of this application, used for building absolute links (e.g. callback URLs). |
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID |
No | (none) | WalletConnect project ID, needed only if wallet-connect based flows are enabled. |
MUX_API_KEY |
No | (none) | Server-only Mux Protocol API key. Used exclusively by Next.js API routes (src/app/api/**) to authenticate upstream requests to the backend. Never exposed to the browser — do not prefix it with NEXT_PUBLIC_. |
MUX_API_SECRET |
No | (none) | Server-only Mux Protocol API secret, paired with MUX_API_KEY and sent alongside it on every upstream request. |
MUX_BACKEND_URL |
No | (none) | Server-only base URL of mux-backend. Used by /api/spending-limits to proxy GET/PUT (spending limits and the real todayUsage). When unset the route returns 503 rather than fabricating usage — the frontend never persists spending limits itself (see getBackendApiBaseUrl() in src/lib/api/config.ts). |
There is no client-visible Mux API key. Project credentials only ever
live in MUX_API_KEY/MUX_API_SECRET and are attached server-side, in
Next.js API routes, to requests made to the backend — the browser talks
only to this app's own same-origin /api/* routes and never holds a Mux
credential.
Testnet vs. mainnet: which backend this frontend talks to is driven
entirely by NEXT_PUBLIC_API_URL (or its aliases above) — point it at a
testnet-configured Mux backend for staging/testnet work, and at the
production backend for mainnet. Separately, the dashboard has an in-app
Testnet/Mainnet switcher (NetworkContext, in the top nav) that scopes
which network's wallets are fetched within that backend — useWallets
sends it as a ?network= query param on /api/wallets, so wallets are
never double-filtered by both a server-side scope and an independent
client-side one. The env var picks the backend; the in-app switcher picks
the network within it. The CI workflow (.github/workflows/ci.yml) sets a
placeholder NEXT_PUBLIC_API_URL only so next build can run without
secrets; it does not reflect a real environment.
Production defaults: when NODE_ENV=production, unset vars with a
documented default (e.g. NEXT_PUBLIC_MUX_API_URL →
https://api.muxprotocol.com) are applied automatically by getEnv(),
so a production deploy with a forgotten env var talks to the real
backend instead of silently serving mock data. Local dev and tests are
unaffected — leaving everything unset there still uses the in-repo
mocks.
NODE_ENV (standard Next.js variable, not defined in .env.example)
also gates some behavior: analytics/tracking hooks
(useAnalytics.ts, useAnalyticsMetrics.ts, useAnalyticsTracking.ts,
recoveryAnalyticsTracking.ts, spendingLimitsTracking.ts) log to the
console outside of production; src/lib/env.ts throws on missing
required vars only when NODE_ENV=production; and the mock/demo
fallbacks in API routes and data hooks
(src/lib/api/runtimeMode.ts, useNotifications.ts, useRecovery.ts)
are disabled when NODE_ENV=production so mock data is never served in a
production build.
Production never silently falls back to mock data. /api/auth/login,
/api/auth/refresh, /api/wallets, /api/wallets/[id],
GET /api/transactions, /api/notifications, /api/overview, and
/api/api-keys (GET/POST/PATCH) all fall back to in-repo mock data
(fake wallets, dashboard stats, API keys, a hardcoded mock bearer/refresh
token) when no backend URL is configured — that's what makes
pnpm run dev, CI, and the /demo routes work with no live backend. In a
production build (NODE_ENV=production) that fallback is disabled: if
NEXT_PUBLIC_API_URL (or its aliases) is missing, those routes return
503 backend_unavailable instead of serving fabricated wallets/analytics/
API keys or accepting the mock token as valid auth. See
isMockFallbackAllowed() in src/lib/api/config.ts.
APIKeyModal's standalone (no-onCreateKey) key generator follows the
same rule client-side, and the wallets sidebar prefetch
(src/lib/walletsPrefetchCache.ts) attaches the caller's session token and
keys its cache entry by it, so a prefetch from one session is never served
to a different session that signs in afterward on the same device.
See docs/frontend-env-vars.md for the full
reference, including which file reads each variable and a manual
verification checklist.
src/lib/api.jsadds request header support withx-request-idand automatic session refresh on401src/utils/fetchWithAuth.ts(used byuseWallets/useWallet/ the Send flow) mirrors that behaviour: on a401it callsPOST /api/auth/refreshonce and retries the original request with the rotated token, only clearing the session and redirecting to/loginif the refresh itself fails (#630)src/lib/session.jspersists auth state and clears stale sessions gracefullysrc/hooks/useSessionGuard.tsis the documented client-side stale-session guard;AuthGuard(wrapped around every real/dashboard/*route byDashboardLayout) delegates its redirect to it, so a middleware-cookie pass with a missing in-memory session still bounces to/login(#624)src/hooks/useWallets.tsadds a wallet query hook that loads wallets from/api/walletssrc/app/api/auth/refresh/route.ts,/api/wallets/route.ts, and/api/wallets/[id]/route.tssimulate auth-protected backend behavior for local testingsrc/app/api/requests/today/route.tsandsrc/app/api/transactions/route.ts(list viaGET, the wallet "Send" flow viaPOST) follow the same pattern as the routes above: they proxy toNEXT_PUBLIC_API_URL(or its aliases) when configured, and fall back to mock data / an in-memory mock transaction otherwise.GET /api/transactionsfilters the mock list by?address=(sender or recipient) and?network=, and returns503 backend_unavailablein a production build with no backend rather than serving mock history- The analytics CSV / JSON export (
/dashboard/analytics) is backed by real, date-scoped transaction records viauseAnalyticsTransactions(GET /analytics/transactions-list), not synthetic objects derived from the aggregated top-assets table. Same production/mock split: in a production build with no backend it surfaces an error instead of silently exporting mock rows. Seesrc/docs/Analytics_Data_Sources.md. - Receive-address QR codes (
src/components/wallet/QrCode.tsx) and the QR download action (src/components/wallet/QRDownloadButton.tsx) encode the real wallet address client-side via theqrcodepackage; no backend call is involved
Server-verified sessions (#621–#628). When NEXT_PUBLIC_API_URL is set:
POST /api/auth/loginproxies to{backend}/auth/loginand writes the backend-issued session token to an HttpOnly,SameSite=Lax,Secure(in production)mux_auth_tokencookie set from the route'sSet-Cookieresponse — neverdocument.cookie(#627).POST /api/auth/refreshproxies to{backend}/auth/refresh, forwarding the caller'sAuthorizationheader / session cookie, and rotates themux_auth_tokencookie from the response (#626). Without a backend it only mints the mock token outside production.- The Next.js middleware verifies the token against
GET {backend}/auth/sessionon every/dashboardrequest — the client-setmux_auth_sessionmarker cookie is only trusted in mock mode (no backend), and now carries; Secureon HTTPS. - Any bearer-token block in the login response is persisted to
sessionStorage(neverlocalStorage) viasrc/lib/session.jssosrc/lib/api.jsandsrc/utils/fetchWithAuth.tscan attachAuthorizationheaders and refresh on401(#628, #630). Both read the samemux-auth-sessionkey, souseWalletssends the tokenAuthContextactually stored (#629). signOut()callsPOST /api/auth/logoutto clear the HttpOnly cookie and the stored bearer session.
A production build with no backend refuses mock sign-in / refresh with
503 backend_unavailable (#625). See
docs/auth-local-setup.md.
No silent mock success in production. API routes that fall back to
in-repo mock data (/api/auth/login, /api/notifications, …) do so only
outside production. A production build with no backend configured returns
503 instead of mock data, so a misconfiguration is visible rather than
masked. The shared rule lives in src/lib/api/runtimeMode.ts.
Run unit/component smoke tests with:
pnpm testRun Playwright end-to-end smoke tests (login, wallet monitoring, and wallet send/receive, desktop and mobile viewports) with:
pnpm exec playwright install --with-deps chromium
pnpm run test:e2eCI runs this same suite on every push and PR (the e2e-tests job in
.github/workflows/ci.yml), alongside typecheck, Vitest, and build.
See tests/e2e/README.md for what's covered and a
manual verification checklist.
Root-level .md files are kept to just this README.md. Deeper
reference docs (env vars, auth setup, analytics data sources, CI
typecheck/build verification, etc.) live under docs/ so they
stay easy to find and don't clutter the repo root as features evolve.
- The dashboard is developer-focused, not end-user focused
- Backend handles wallets and transactions; the dashboard is a monitoring and management tool
- Makes it simple to observe, control, and integrate Mux-powered wallets
- Per-key usage analytics
- Webhooks and notifications for SDK events
Team access management— basic admin/developer member management is in at/dashboard/settings/team(/api/team); seedocs/team-access-and-audit-log.mdAudit logs for all wallet and API activity—/api/activitynow follows the same production/mock split as the rest of the app instead of always serving mock data; seedocs/team-access-and-audit-log.md