Three docs updates landing the institutional knowledge from today's
Memoro voice recording deploy:
- docs/MAC_MINI_SERVER.md: architecture diagram updated to show the
two-tunnel setup (cloudflared on the Mac Mini for *.mana.how
except gpu-*, plus a separate cloudflared running as a Windows
Service on the GPU box for gpu-*.mana.how). New "GPU Tunnel
(mana-gpu-server)" section explains how to add hostnames in the
Cloudflare dashboard, the standard 502 debug ladder (DNS misroute,
service stopped, scheduled task crashed, missing public hostname),
and how the API key flows from the Windows .env through Mac Mini
.env to the mana-web container.
- docs/ENVIRONMENT_VARIABLES.md: STT section updated to reflect that
MANA_STT_URL/API_KEY are now wired into the mana-web container via
docker-compose.macmini.yml (committed in 42bd2a3a0). Health-check
command added; cross-link to MAC_MINI_SERVER.md for the debug ladder.
- docs/POSTMORTEM_2026-04-07.md (new): full incident timeline of
today's deploy. Six root causes (tunnel never started, DB wiped
without re-push, untracked module-registry files, uncommitted
Dockerfile heap bump, missing compose env vars, /offline prerender
500). Three "what went poorly" honest assessments (premature P0
alarm, miscounted commits, clumsy stash dance). Action items split
by priority — high priority is the clean-clone build CI job, which
would have caught half the issues today.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.7 KiB
Environment Variables Guide
This document explains the centralized environment variable system for the Mana monorepo.
Quick Start
# After cloning the repo, install dependencies (auto-generates .env files)
pnpm install
# Or manually generate .env files
pnpm setup:env
That's it! All app-specific .env files are generated from .env.development.
How It Works
.env.development # Central config (committed)
│
▼
scripts/generate-env.mjs # Transforms variables
│
▼
apps/**/apps/**/.env # Generated files (gitignored)
The generator reads .env.development and creates app-specific .env files with the correct prefixes for each platform:
| Platform | Prefix | Example |
|---|---|---|
| Expo (mobile) | EXPO_PUBLIC_ |
EXPO_PUBLIC_SUPABASE_URL |
| SvelteKit (web) | PUBLIC_ |
PUBLIC_SUPABASE_URL |
| Hono/Bun (server) | None | DATABASE_URL |
File Locations
Source File
.env.development- Single source of truth, committed to git
Generated Files (gitignored)
services/mana-auth/.envapps/chat/apps/server/.envapps/chat/apps/mobile/.envapps/chat/apps/web/.envapps/mana/apps/mobile/.envapps/mana/apps/web/.envapps/cards/apps/server/.envapps/cards/apps/web/.envapps/*/apps/server/.env(all apps with compute servers)apps/*/apps/web/.env(all web apps)apps/*/apps/mobile/.env(all mobile apps)
Variable Reference
Shared Variables
| Variable | Description | Used By |
|---|---|---|
MANA_AUTH_URL |
Auth service URL | All apps |
JWT_PRIVATE_KEY |
JWT signing key | mana-auth |
JWT_PUBLIC_KEY |
JWT verification key | All backends |
POSTGRES_USER |
Database user | Docker, backends |
POSTGRES_PASSWORD |
Database password | Docker, backends |
REDIS_HOST |
Redis host | mana-auth |
REDIS_PORT |
Redis port | mana-auth |
REDIS_PASSWORD |
Redis password | mana-auth |
Mana Auth Service
| Variable | Description | Default |
|---|---|---|
MANA_AUTH_PORT |
Service port | 3001 |
MANA_AUTH_DATABASE_URL |
PostgreSQL connection string | - |
JWT_ACCESS_TOKEN_EXPIRY |
Access token TTL | 15m |
JWT_REFRESH_TOKEN_EXPIRY |
Refresh token TTL | 7d |
JWT_ISSUER |
JWT issuer claim | mana |
JWT_AUDIENCE |
JWT audience claim | mana |
STRIPE_SECRET_KEY |
Stripe secret key | - |
STRIPE_PUBLISHABLE_KEY |
Stripe publishable key | - |
STRIPE_WEBHOOK_SECRET |
Stripe webhook secret | - |
CORS_ORIGINS |
Allowed CORS origins | - |
RATE_LIMIT_TTL |
Rate limit window (seconds) | 60 |
RATE_LIMIT_MAX |
Max requests per window | 100 |
Chat Project
| Variable | Description | Default |
|---|---|---|
CHAT_BACKEND_PORT |
Backend service port | 3002 |
CHAT_DATABASE_URL |
PostgreSQL connection string | - |
AZURE_OPENAI_ENDPOINT |
Azure OpenAI endpoint URL | - |
AZURE_OPENAI_API_KEY |
Azure OpenAI API key | - |
AZURE_OPENAI_API_VERSION |
API version | 2024-12-01-preview |
CHAT_SUPABASE_URL |
Supabase project URL | - |
CHAT_SUPABASE_ANON_KEY |
Supabase anonymous key | - |
Mana Project
| Variable | Description |
|---|---|
MANA_SUPABASE_URL |
Supabase project URL |
MANA_SUPABASE_ANON_KEY |
Supabase anonymous key |
Cards Project
| Variable | Description | Default |
|---|---|---|
CARDS_BACKEND_PORT |
Backend service port | 3004 |
CARDS_SUPABASE_URL |
Supabase project URL | - |
CARDS_SUPABASE_ANON_KEY |
Supabase anonymous key | - |
Speech-to-Text (mana-stt)
Used by the unified Mana web app's voice features (Memoro recording, Dreams voice capture, etc).
The browser never talks to mana-stt directly — requests go through the SvelteKit server-side proxy
(/api/v1/memoro/transcribe, /api/v1/dreams/transcribe) which attaches the API key from
MANA_STT_API_KEY. Keep that key out of the browser bundle.
| Variable | Description | Default |
|---|---|---|
STT_URL |
Public mana-stt URL — generates MANA_STT_URL for the web app |
https://gpu-stt.mana.how |
MANA_STT_API_KEY |
API key for mana-stt. Never commit a real value. | (empty) |
Where to obtain a key:
- Production (Mac Mini):
MANA_STT_API_KEYis read from~/projects/mana-monorepo/.envon the Mac Mini and injected into themana-webcontainer bydocker-compose.macmini.yml(themana-webservice block, alongsideMANA_STT_URL=https://gpu-stt.mana.how). To rotate, update the.envvalue and recreate the container withdocker compose -f docker-compose.macmini.yml up -d --no-deps --force-recreate mana-web. - Local dev: paste the dev key into your local
apps/mana/apps/web/.envafter runningpnpm setup:env(the generator only writes an empty placeholder). Ask in#mana-devor pull from the team's password manager undermana-stt → web-key. - Source of truth:
services/mana-stt/.envon the Windows GPU box, in theAPI_KEYSvariable. Each entry is<random>:<name>and gets rate-limited per key. - Adding a new key: SSH to the Windows GPU box (
ssh mana-gpu), append a new entry toC:\mana\services\mana-stt\.envAPI_KEYS, restart theManaSTTscheduled task. Use a fresh key per consumer (mana-web,chat-server, etc.) so they can be revoked individually.
Endpoint: https://gpu-stt.mana.how — Cloudflare Tunnel mana-gpu-server (token-managed,
runs as a Windows Service on the GPU box, not as a route in the Mac Mini's cloudflared).
The tunnel terminates at localhost:3020 on the Windows host.
Health check:
curl https://gpu-stt.mana.how/health
# → {"status":"healthy","whisper_loaded":true,"whisperx":true,...}
If this returns 502, see "GPU Tunnel" in docs/MAC_MINI_SERVER.md for the standard
debug ladder.
Adding New Variables
Step 1: Add to .env.development
# In .env.development
MY_NEW_PROJECT_API_KEY=your-api-key
MY_NEW_PROJECT_URL=https://api.example.com
Step 2: Update the Generator Script
Edit scripts/generate-env.mjs and add your app config:
// In APP_CONFIGS array
{
path: 'apps/my-project/apps/mobile/.env',
vars: {
// For Expo, add EXPO_PUBLIC_ prefix
EXPO_PUBLIC_API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY,
EXPO_PUBLIC_API_URL: (env) => env.MY_NEW_PROJECT_URL,
},
},
{
path: 'apps/my-project/apps/web/.env',
vars: {
// For SvelteKit, add PUBLIC_ prefix
PUBLIC_API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY,
PUBLIC_API_URL: (env) => env.MY_NEW_PROJECT_URL,
},
},
{
path: 'apps/my-project/apps/server/.env',
vars: {
// For Hono/Bun servers, no prefix needed
API_KEY: (env) => env.MY_NEW_PROJECT_API_KEY,
API_URL: (env) => env.MY_NEW_PROJECT_URL,
},
},
Step 3: Regenerate
pnpm setup:env
Local Overrides
If you need to override variables locally without affecting others:
- The generated
.envfiles are gitignored - You can manually edit them after generation
- Or create
.env.localfiles (also gitignored) that some frameworks auto-load
Note: Running pnpm setup:env will overwrite your changes, so use .env.local for persistent overrides.
Docker Integration
The root .env.development is also used by Docker Compose:
# Start all services with shared env
pnpm docker:up:all
Docker services read from:
- Root
.env.developmentfor shared values - Service-specific
.envfiles for service-specific values
Troubleshooting
"Variable is undefined" Error
- Check if the variable exists in
.env.development - Run
pnpm setup:envto regenerate - Restart your dev server (env changes require restart)
Generated File Has Wrong Value
- Check the mapping in
scripts/generate-env.mjs - Ensure the source variable name matches exactly
- Run
pnpm setup:envagain
New App Not Getting Generated
- Add app config to
APP_CONFIGSinscripts/generate-env.mjs - Ensure the target directory exists
- Run
pnpm setup:env
Expo Not Picking Up Changes
Expo caches environment variables. Clear the cache:
cd apps/<project>/apps/mobile
npx expo start -c
Security Notes
.env.developmentcontains development-only values- Never put production secrets in this file
- The JWT keys in
.env.developmentare for local development only - Use separate secrets management for production (1Password, AWS Secrets Manager, etc.)
Migration from Old System
If you have existing .env files with real values:
- Copy important values to
.env.development - Delete the old
.envfiles - Run
pnpm setup:env - Verify apps still work
The old .env.example files can remain as documentation.