Skip to main content

APP_VAR Dynamic Substitution V2 (User + Developer Guide)

Document Scope

  • Feature: Dynamic APP_VAR substitution for variables/content
  • Version: Consolidated and updated on 2026-02-11
  • Audience: Users + Developers (single source of truth)

1) User Guide

What APP_VAR does

APP_VAR_* tokens are placeholders replaced with live values.

Example:

  • Input: Hello APP_VAR_USER_EMAIL
  • Output (saved or rendered): Hello user@example.com

Where replacement happens

  • Backend replacement (save-time): value is replaced before persistence when backend flag is ON.
  • Frontend replacement (view-time): value is replaced in UI render (Peek Drawer/chat render), not persisted.

Current tokens you can use

Backend tokens (save-time):

  • APP_VAR_CURRENT_CHANNEL
  • APP_VAR_CURRENT_WORKSPACE
  • APP_VAR_USER_ID
  • APP_VAR_USER_EMAIL
  • APP_VAR_USER_EMAIL_ACCOUNT
  • APP_VAR_USER_NAME (user name from latest mm_user_data / auth fallback)
  • APP_VAR_NAME (current variable name/path where token is rendered)

Frontend tokens (view-time):

  • APP_VAR_BASE_URL
  • APP_VAR_BROWSER_LOCALE
  • APP_VAR_CURRENT_TIMEZONE
  • APP_VAR_CURRENT_TIME

Control/negative-test token:

  • APP_VAR_BAD_PIPELINE (intentionally invalid; expected to remain unresolved)

Practical examples

Deep-link template (dynamic domain + channel/workspace):

APP_VAR_BASE_URL/app/channel/APP_VAR_CURRENT_CHANNEL?newSession=true&agent=Master%20Teresa%2C%20Fast%20Solution%20Discovery&import=APP_VAR_CURRENT_CHANNEL&workspace=APP_VAR_CURRENT_WORKSPACE

Deep-link template (canonical, agent acronym):

APP_VAR_BASE_URL/app/channel/APP_VAR_CURRENT_CHANNEL?newSession=true&agent=trinity&import=APP_VAR_CURRENT_CHANNEL&workspace=APP_VAR_CURRENT_WORKSPACE

External deep-link (create new session using agent name, no UUID):

https://SEU_DOMINIO/app?newSession=true&agent=Master%20Teresa%2C%20Fast%20Solution%20Discovery&workspace=WORKSPACE_UUID

External deep-link (local example using agent name):

http://localhost:8080/app?newSession=true&agent=Master%20Teresa%2C%20Fast%20Solution%20Discovery&workspace=2b3ef051-5a7a-4a41-8f34-07e5f4c537ae

External deep-link (without workspace param):

https://SEU_DOMINIO/app?newSession=true&agent=Master%20Teresa%2C%20Fast%20Solution%20Discovery

Without workspace, app fallback is:

  • use saved/selected workspace for that user;
  • if no saved state, use first available workspace;
  • fail only when user has no accessible workspace.

Identity template:

Email=APP_VAR_USER_EMAIL Account=APP_VAR_USER_EMAIL_ACCOUNT UserName=APP_VAR_USER_NAME VariableName=APP_VAR_NAME

Troubleshooting (User)

  • If backend tokens are not replaced on save:
    • Verify backend flag is ON (app_var_substitution_backend_enabled).
    • Confirm variable creation path goes through Add Agent Context / head function.
  • If frontend tokens are visible in UI:
    • Hard refresh browser.
    • Clear cache key app_var_registry_cache_v2 and reload.
    • Check token in rendered view (Peek Drawer/chat), not raw editor text.
  • APP_VAR_BAD_PIPELINE unresolved is expected behavior.

2) Developer Guide

High-level architecture

Processing order:

  1. Include expansion (<include />)
  2. Context substitution (APP_VAR_CURRENT_CHANNEL, etc.)
  3. Backend APP_VAR substitution (if backend flag ON)
  4. Persist to mvpl_variables
  5. Frontend APP_VAR substitution at render-time

Registry model

Table: public.mvpl_variables_dynamic

Relevant fields:

  • var_name (PK)
  • var_category (backend | frontend)
  • resolver_pipeline (jsonb, required)
  • active (bool)
  • description, example_value

V2 note:

  • Legacy resolver_type and resolver_config are deprecated/removed for V2 path.
  • Resolver is pipeline-based only.

Pipeline step types

  • context: read from structured context (user, channel, workspace, message, browser, time)
  • db: whitelisted table/columns, context-bound filters only
  • variable: read variable from scoped channel/workspace
  • transform: safe ops (split, lower, upper, substring, json_get, coalesce)

Security model

  • Fail-closed: unresolved/invalid pipeline keeps placeholder.
  • DB step whitelist + context anchoring enforced.
  • User-scoped constraints for sensitive lookups.
  • Registry write access restricted by RLS/admin policy; read allowed for authenticated users.

Feature flags

Table: public.mvpl_parameters

  • app_var_substitution_backend_enabled (effective backend gate)
  • app_var_substitution_frontend_enabled (reserved/optional gate depending on UI policy)

Expected config shape:

{ "type": "boolean", "value": true }

Frontend reliability behavior (critical)

For core frontend tokens (BASE_URL, BROWSER_LOCALE, CURRENT_TIMEZONE, CURRENT_TIME):

  • UI first loads registry from get-app-var-registry.
  • If cached registry is missing core vars, it force-refreshes.
  • If registry fetch fails, UI applies local runtime fallback so core tokens still resolve.
  • This guarantees APP_VAR_BASE_URL resolves to window.location.origin at render-time.

Key implementation files

Backend:

  • supabase/functions/_shared/app-var-substitution/backend-resolver.ts
  • supabase/functions/_shared/app-var-substitution/backend-substitution.ts
  • supabase/functions/_shared/app-var-substitution/pipeline.ts
  • supabase/functions/channel-variables/index.ts
  • supabase/functions/get-app-var-registry/index.ts

Frontend:

  • src/hooks/useAppVarSubstitution.ts
  • src/lib/app-var-substitution/frontend-pipeline.ts
  • src/lib/app-var-substitution/frontend-resolver.ts
  • src/lib/api/appVarRegistry.ts
  • src/lib/api/variables.ts
  • src/components/journey/AddContentModal.tsx
  • src/components/canvas/PeekDrawer.tsx

Migrations relevant to this feature

  • supabase/migrations/20260128094000_seed_app_var_substitution_parameters.sql
  • supabase/migrations/20260205164907_add_app_var_base_url_frontend.sql
  • supabase/migrations/20260211154500_reassign_app_var_name_user_name.sql

Path/params are handled in:

  • src/components/layout/MainLayout.tsx
  • src/components/layout/LeftPanel.tsx
  • src/lib/deep-link-utils.ts

Canonical template:

APP_VAR_BASE_URL/app/channel/APP_VAR_CURRENT_CHANNEL?newSession=true&agent=trinity&import=APP_VAR_CURRENT_CHANNEL&workspace=APP_VAR_CURRENT_WORKSPACE

Notes:

  • newSession=true opens Create Session modal.
  • agent accepts UUID, acronym (recommended), or name token.
  • import accepts channel UUID or channel name; using APP_VAR_CURRENT_CHANNEL resolves to current channel UUID.
  • workspace accepts workspace UUID or name; using APP_VAR_CURRENT_WORKSPACE resolves to current workspace UUID.

Validation commands

Focused checks:

npm run test -- src/lib/__tests__/variables-api.test.ts src/lib/app-var-substitution/frontend-resolver.test.ts src/lib/__tests__/app-var-substitution.test.ts tests/unit/edge-functions/app-var-context-builder.test.ts tests/unit/edge-functions/app-var-resolver-pipeline.test.ts

npm run integration:local -- tests/integration/edge-functions/app-var-substitution.integration.test.ts tests/integration/frontend/app-var-frontend-substitution.test.ts

npx env-cmd -f .env.local playwright test tests/e2e/app-var-dynamic-links.spec.ts tests/e2e/app-var-deeplink-integration.spec.ts tests/e2e/app-var-frontend-runtime.spec.ts

Known expected behavior

  • APP_VAR_BAD_PIPELINE remains unresolved by design (negative-path coverage).
  • Backend placeholders remain unchanged when backend flag is OFF.
  • Frontend placeholders resolve only in rendered UI, not raw stored markdown.

3) Quick Reference

One variable to test everything

[Backend Vars]
channel: APP_VAR_CURRENT_CHANNEL
workspace: APP_VAR_CURRENT_WORKSPACE
user_id: APP_VAR_USER_ID
user_email: APP_VAR_USER_EMAIL
user_email_account: APP_VAR_USER_EMAIL_ACCOUNT
user_name: APP_VAR_USER_NAME
variable_name_path: APP_VAR_NAME
bad_pipeline_control: APP_VAR_BAD_PIPELINE

[Frontend Vars]
base_url: APP_VAR_BASE_URL
locale: APP_VAR_BROWSER_LOCALE
timezone: APP_VAR_CURRENT_TIMEZONE
time_iso: APP_VAR_CURRENT_TIME

[Deep Link Template]
APP_VAR_BASE_URL/app/channel/APP_VAR_CURRENT_CHANNEL?newSession=true&agent=Master%20Teresa%2C%20Fast%20Solution%20Discovery&import=APP_VAR_CURRENT_CHANNEL&workspace=APP_VAR_CURRENT_WORKSPACE