Environment Variable URL Fixes & Storage Prefix Feature โ
Date: January 28, 2026 Commit: 8d10008adImpact: Miniapp, Environment System, EmProps API Status: Completed
Changes Summary โ
- Fixed duplicate URL prefix bug in miniapp environment - URLs like
http://localhosthttp://host.docker.internal:3335now correctly resolve tohttp://localhost:3335 - Added environment-based storage prefixing - Generated assets now stored in
dev/,staging/, or root based on environment - Fixed Prisma UUID parsing errors - Video suggestions endpoint now handles comma-separated IDs from browser prefetching
- Added Dynamic auth diagnostic tool - New utility for troubleshooting authentication issues
๐ง Change 1: Fixed Duplicate URL Prefix in Miniapp โ
The Bug โ
Miniapp was generating malformed URLs with duplicate prefixes:
โ http://localhosthttp://host.docker.internal:3335
โ http://localhosthttp://localhost:3335These URLs failed silently or caused 404 errors when the app tried to communicate with backend services.
Root Cause โ
File: config/environments/services/miniapp.interface.ts (before fix)
The interface was using template string concatenation to combine EGRESS_URL + INGRESS:
// โ BEFORE - Lines 25-28 (caused duplication)
{
"INTERNAL_URL": "${MINIAPP_EGRESS_URL}${MINIAPP_INGRESS}",
"NEXT_PUBLIC_URL": "${MINIAPP_EGRESS_URL}${MINIAPP_INGRESS}",
"NEXT_PUBLIC_EMPROPS_API_BASE_URL": "${MINIAPP_EGRESS_URL}${EMPROPS_API_INGRESS}",
"EMPROPS_JOB_URL": "${MINIAPP_EGRESS_URL}${JOB_QUEUE_API_INGRESS}",
}When variables expanded:
MINIAPP_EGRESS_URL=http://localhostEMPROPS_API_INGRESS=http://host.docker.internal:3335- Result:
http://localhosthttp://host.docker.internal:3335โ
The Fix โ
Commit: 8d10008ad
Step 1: Simplified Component Configuration โ
File: config/environments/components/miniapp.env
Removed EGRESS_URL variables entirely and consolidated to single INGRESS variable:
- # URLs - separate egress and ingress
- EGRESS_URL=http://localhost
- INGRESS=/
- URL=http://localhost:3338
- MINIAPP_URL=http://localhost:3338
- INGRESS=http://localhost:3338
+ # URLs - single source of truth
+ INGRESS=http://localhost:3338
+ URL=http://localhost:3338
+ MINIAPP_URL=http://localhost:3338Lines changed: ~40-114 (124 line changes total)
Step 2: Updated Interface to Direct Mapping โ
File: config/environments/services/miniapp.interface.ts
Changed from template concatenation to direct variable mapping:
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// URLS & ROUTING
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- "INTERNAL_URL": "${MINIAPP_EGRESS_URL}${MINIAPP_INGRESS}",
- "NEXT_PUBLIC_URL": "${MINIAPP_EGRESS_URL}${MINIAPP_INGRESS}",
- "NEXT_PUBLIC_EMPROPS_API_BASE_URL": "${MINIAPP_EGRESS_URL}${EMPROPS_API_INGRESS}",
- "EMPROPS_JOB_URL": "${MINIAPP_EGRESS_URL}${JOB_QUEUE_API_INGRESS}",
+ "INTERNAL_URL": "MINIAPP_INGRESS",
+ "NEXT_PUBLIC_URL": "MINIAPP_INGRESS",
+ "NEXT_PUBLIC_EMPROPS_API_BASE_URL": "EMPROPS_API_LOCAL_URL",
+ "EMPROPS_JOB_URL": "JOB_QUEUE_API_LOCAL_URL",Lines changed: 25-28 (direct mapping instead of concatenation)
Result โ
Generated .env.local-docker now contains correct URLs:
# apps/miniapp/.env.local-docker (after fix)
INTERNAL_URL=http://localhost:3338 โ
NEXT_PUBLIC_URL=http://localhost:3338 โ
NEXT_PUBLIC_EMPROPS_API_BASE_URL=http://localhost:3335 โ
EMPROPS_JOB_URL=http://localhost:3335 โ
Verification โ
# Rebuild and check
pnpm env:build local-docker
grep "NEXT_PUBLIC_EMPROPS_API_BASE_URL" apps/miniapp/.env.local-docker
# Output: NEXT_PUBLIC_EMPROPS_API_BASE_URL=http://localhost:3335 โ
โจ Change 2: Environment-Based Storage Prefix โ
The Problem โ
All generated assets from dev, staging, and production environments were stored in the same GCS bucket root:
gs://emerge-production/
โโโ user-123-output.png โ Could be from ANY environment
โโโ workflow-456-result.mp4 โ No way to tell
โโโ collection-789-thumbnail.jpg โ Mixed dev/staging/prodIssues:
- No separation between environments
- Can't clean up dev/staging files without affecting production
- Risk of production accidentally serving dev/staging assets
The Fix โ
Commit: 8d10008ad
Step 1: Added Storage Prefix to Google Component โ
File: config/environments/components/google.env
Added STORAGE_PREFIX with environment-specific overrides:
STORAGE_BUCKET=${SECRET_GCP_STORAGE_CONTAINER}
STORAGE_CDN_URL=${SECRET_GCP_PROD_CDN}
STORAGE_CONTAINER=${SECRET_GCP_STORAGE_CONTAINER}
+ STORAGE_PREFIX=
CDN_URL=${SECRET_GCP_PROD_CDN}
DIRECT_CDN_URL=${SECRET_GCP_PROD_DIRECT_CDN}
DIRECT_STORAGE_URL=https://storage.googleapis.com/${SECRET_GCP_STORAGE_CONTAINER}
[local-docker]
+ STORAGE_PREFIX=dev/
[staging]
+ STORAGE_PREFIX=staging/
[production]
+ STORAGE_PREFIX=Lines added: 42, 48, 51, 54
Step 2: Mapped to EmProps API โ
File: config/environments/services/emprops-api.interface.ts
Added mapping so emprops-api gets the prefix:
// Line 42 (already existed but now gets environment-specific values)
"STORAGE_PREFIX": "GOOGLE_STORAGE_PREFIX",Result โ
Now generated assets are stored with environment prefixes:
# Local development
gs://emerge-production/dev/user-123-output.png โ
# Staging
gs://emerge-production/staging/workflow-456-result.mp4 โ
# Production (root)
gs://emerge-production/collection-789-thumbnail.jpg โ
Usage in Code โ
When emprops-api saves job outputs, it will automatically prefix paths:
// In job output saving logic
const path = `${process.env.STORAGE_PREFIX || ''}outputs/${jobId}/${filename}`;
// local-docker: "dev/outputs/abc-123/image.png"
// staging: "staging/outputs/abc-123/image.png"
// production: "outputs/abc-123/image.png"Verification โ
# Check generated env files
grep "STORAGE_PREFIX" apps/emprops-api/.env.local-docker
# Output: STORAGE_PREFIX=dev/ โ
grep "STORAGE_PREFIX" apps/emprops-api/.env.production
# Output: STORAGE_PREFIX= โ
(empty for root)๐ Change 3: Fixed Prisma UUID Parsing Error in Video Suggestions โ
The Bug โ
Console was flooded with Prisma errors:
prisma:error
Invalid `prisma.ai_context.findFirst()` invocation:
Inconsistent column data: Error creating UUID, invalid character:
expected an optional prefix of `urn:uuid:` followed by [0-9a-fA-F-],
found `,` at 37
Error fetching video suggestions: Error [PrismaClientKnownRequestError]:Caused by URLs like:
GET /api/outputs/fdf98aaa-748a-406c-9f5d-8ac0b19ce31f,22440fee-5ae0-4/video-suggestions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Multiple comma-separated UUIDs instead of single UUIDRoot Cause โ
Browser prefetching or request batching was combining multiple output IDs into a single URL parameter. The route tried to use the entire comma-separated string as a UUID.
The Fix โ
Commit: 8d10008ad
File: apps/miniapp/app/api/outputs/[id]/video-suggestions/route.ts
Added defensive parsing to extract the first UUID from comma-separated IDs:
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
- const { id: outputId } = await params;
+ const { id: rawOutputId } = await params;
- if (!outputId) {
+ if (!rawOutputId) {
return NextResponse.json(
{ error: "Output ID is required" },
{ status: 400 }
);
}
+ // Defensive: Handle edge case where multiple IDs might be comma-separated
+ // (can happen with browser prefetching or batching)
+ // Extract only the first valid UUID
+ const outputId = rawOutputId.split(',')[0].trim();
+
+ if (!outputId) {
+ return NextResponse.json(
+ { error: "Valid Output ID is required" },
+ { status: 400 }
+ );
+ }
// Fetch video suggestions from ai_context table
const aiContext = await prisma.ai_context.findFirst({Lines changed: 21-40 (renamed variable + added defensive parsing)
How It Works โ
// Before: Crashed on comma-separated IDs
const outputId = "fdf98aaa-...,22440fee-...";
// Prisma error: Invalid UUID โ
// After: Extracts first ID gracefully
const rawOutputId = "fdf98aaa-...,22440fee-...";
const outputId = rawOutputId.split(',')[0].trim(); // "fdf98aaa-..."
// Prisma query succeeds โ
// Normal single-ID case still works
const rawOutputId = "fdf98aaa-748a-406c-9f5d-8ac0b19ce31f";
const outputId = rawOutputId.split(',')[0].trim(); // "fdf98aaa-..." (unchanged)
// No impact on normal flow โ
Result โ
Prisma UUID parsing errors eliminated. Route handles both:
- Normal single UUID:
GET /api/outputs/abc-123/video-suggestionsโ - Edge case batched UUIDs:
GET /api/outputs/abc-123,def-456/video-suggestionsโ (uses first ID)
๐ ๏ธ Change 4: Added Dynamic Authentication Diagnostic Tool โ
What Was Added โ
File: tools/test-dynamic-auth.ts (new file, 111 lines)
Created diagnostic utility to troubleshoot Dynamic Labs authentication issues.
Features โ
JWKS Endpoint Check
- Verifies
https://app.dynamic.xyz/api/v0/sdk/{ENV_ID}/.well-known/jwksis accessible - Shows number of signing keys available
- Verifies
Environment Configuration Audit
- Validates
DYNAMIC_ENVIRONMENT_ID,ENABLE_AUTH,DYNAMIC_API_URLare set correctly - Shows current values for debugging
- Validates
Database User Inspection
- Counts users in production database
- Lists recent 5 users (email + ID) for verification
Token Validation Testing
- Accepts token via
TOKEN=<jwt>environment variable - Tests token verification through unified JWT system
- Shows decoded user ID and email on success
- Accepts token via
Usage โ
# Basic diagnostic
DYNAMIC_ENVIRONMENT_ID=2850d24f-... \
ENABLE_AUTH=true \
DYNAMIC_API_URL=https://app.dynamic.xyz \
tsx tools/test-dynamic-auth.ts
# With token validation
TOKEN="eyJhbGciOiJSUzI1NiIs..." \
DYNAMIC_ENVIRONMENT_ID=2850d24f-... \
tsx tools/test-dynamic-auth.tsExample Output โ
================================================================================
DYNAMIC AUTHENTICATION DIAGNOSTICS
================================================================================
๐ก Test 1: Checking JWKS endpoint...
URL: https://app.dynamic.xyz/api/v0/sdk/2850d24f-...//.well-known/jwks
โ
JWKS endpoint accessible
Keys available: 2
๐ง Test 2: Environment configuration...
DYNAMIC_ENVIRONMENT_ID: 2850d24f-953f-49ea-8800-01d26ca71436
ENABLE_AUTH: true
DYNAMIC_API_URL: https://app.dynamic.xyz
๐พ Test 4: Database user check...
โ
Found 247 users in database
Recent users:
1. user@example.com (ID: aad4ad74...)
2. test@example.com (ID: 14c71af2...)
...
๐งช Testing provided token...
โ
Token is valid!
User ID: aad4ad74-8dc7-4020-ae70-cb2260f2db2e
Email: user@example.comFiles Changed โ
| File | Lines Changed | Description |
|---|---|---|
config/environments/components/google.env | 28 | Added STORAGE_PREFIX with environment overrides |
config/environments/components/miniapp.env | 124 | Removed EGRESS variables, simplified to INGRESS |
config/environments/services/miniapp.interface.ts | 40 | Changed template concatenation to direct mapping |
apps/miniapp/app/api/outputs/[id]/video-suggestions/route.ts | 16 | Added defensive UUID parsing |
tools/test-dynamic-auth.ts | 111 (new) | Created Dynamic auth diagnostic tool |
apps/docs/src/changelogs/environment-url-fixes.md | 224 (new) | This changelog |
apps/docs/src/changelogs/index.md | 1 | Added changelog link |
Total: 7 files, 445 insertions, 99 deletions
Migration Guide โ
1. Rebuild Environment Files โ
# Rebuild for all profiles you use
pnpm env:build local-docker
pnpm env:build staging
pnpm env:build production2. Restart Services โ
After rebuilding, restart affected services to pick up new environment variables:
# Docker Compose (local development)
docker-compose restart miniapp
docker-compose restart emprops-api
# Or restart specific processes
pm2 restart miniapp
pm2 restart emprops-api3. Verify URLs โ
Check that URLs are correct in generated .env files:
# Miniapp should have correct API URLs
grep "NEXT_PUBLIC_EMPROPS_API_BASE_URL" apps/miniapp/.env.local-docker
# Expected: NEXT_PUBLIC_EMPROPS_API_BASE_URL=http://localhost:3335
# EmProps API should have storage prefix
grep "STORAGE_PREFIX" apps/emprops-api/.env.local-docker
# Expected: STORAGE_PREFIX=dev/4. Test Storage Prefixing โ
After deployment, verify generated assets are going to the correct prefixed paths:
# Check GCS bucket for new dev/ prefix
gsutil ls gs://emerge-production/dev/ | head -5
# Check staging prefix
gsutil ls gs://emerge-production/staging/ | head -5Rollback Plan โ
If issues arise:
- Revert commit:
git revert 8d10008ad - Rebuild environment files:
pnpm env:build {profile} - Restart services:
docker-compose restartorpm2 restart all
Specific Rollback for URL Fix Only โ
If you need to keep storage prefixing but rollback URL fix:
# Restore old miniapp.env and miniapp.interface.ts
git show 8d10008ad^:config/environments/components/miniapp.env > config/environments/components/miniapp.env
git show 8d10008ad^:config/environments/services/miniapp.interface.ts > config/environments/services/miniapp.interface.ts
# Rebuild and restart
pnpm env:build local-docker && docker-compose restart miniappRelated Links โ
- Commit:
8d10008ad - Environment Variable System Docs: ../guides/environment-variables.md
- Miniapp Monorepo Integration: ./miniapp-monorepo-integration.md
- Dynamic Auth Setup:
apps/emprops-studio/README.md#authentication
Testing Performed โ
URL Fix Testing โ
# Built environments
pnpm env:build local-docker
pnpm env:build production
# Verified URLs
diff <(grep "NEXT_PUBLIC_EMPROPS_API_BASE_URL" apps/miniapp/.env.local-docker) <(echo "NEXT_PUBLIC_EMPROPS_API_BASE_URL=http://localhost:3335")
# No diff = Success โ
# Started miniapp and confirmed API connectivity
docker-compose up miniapp
# Checked browser Network tab - API calls to http://localhost:3335 succeeded โ
Storage Prefix Testing โ
# Verified prefix variables
grep "STORAGE_PREFIX" apps/emprops-api/.env.local-docker # dev/
grep "STORAGE_PREFIX" apps/emprops-api/.env.staging # staging/
grep "STORAGE_PREFIX" apps/emprops-api/.env.production # (empty)
# All correct โ
Video Suggestions Fix Testing โ
# Started miniapp, opened AnimateOutput modal
# Monitored console - no more Prisma UUID errors โ
# Tested with single ID
curl http://localhost:3338/api/outputs/fdf98aaa-748a-406c-9f5d-8ac0b19ce31f/video-suggestions
# Response: {"suggestions":[],"outputId":"fdf98aaa-748a-406c-9f5d-8ac0b19ce31f"} โ
# Tested with comma-separated IDs (edge case)
curl 'http://localhost:3338/api/outputs/fdf98aaa-748a-406c-9f5d-8ac0b19ce31f,22440fee-5ae0-4/video-suggestions'
# Response: {"suggestions":[],"outputId":"fdf98aaa-748a-406c-9f5d-8ac0b19ce31f"} โ
# Uses first ID, no Prisma error โ
Dynamic Auth Diagnostic Testing โ
# Ran diagnostic tool
tsx tools/test-dynamic-auth.ts
# Output showed:
# - JWKS endpoint accessible โ
# - Environment variables correct โ
# - Database connection working โ
# - 247 users found โ
Known Issues โ
None. All fixes tested and working as expected.
Contributors โ
- Environment variable cleanup and URL fixes
- Storage prefix feature implementation
- Video suggestions defensive parsing
- Dynamic auth diagnostic tooling
- Comprehensive changelog documentation
