Environment Variable Interpolation
Use the {{$.env.VAR_NAME}} syntax to pass dynamic values into page.ai() instructions while keeping the cache stable across environments.
Donobu provides a built-in interpolation syntax for referencing environment variables inside page.ai() instructions. This is the recommended way to pass dynamic values — credentials, usernames, hostnames — into AI flows while keeping the cache stable across environments.
The syntax
Use {{$.env.VAR_NAME}} inside an instruction string:
await page.ai(
'Log in with email {{$.env.TEST_EMAIL}} and password {{$.env.TEST_PASSWORD}}',
{
envVars: ['TEST_EMAIL', 'TEST_PASSWORD'],
},
);
At runtime, Donobu substitutes {{$.env.TEST_EMAIL}} with the value of the TEST_EMAIL environment variable before passing the instruction to the AI.
Why this matters for caching
The cache key is built from the literal instruction string, not the resolved value. This means:
// ✅ Cache key: "Log in with email {{$.env.TEST_EMAIL}} and password {{$.env.TEST_PASSWORD}}"
// Same key on staging (user@staging.example.com) and production (user@prod.example.com)
await page.ai(
'Log in with email {{$.env.TEST_EMAIL}} and password {{$.env.TEST_PASSWORD}}',
{
envVars: ['TEST_EMAIL', 'TEST_PASSWORD'],
},
);
// ❌ Cache key: "Log in with email user@staging.example.com and password s3cret"
// Different key per environment — creates one cache entry per unique value
await page.ai(
`Log in with email ${process.env.TEST_EMAIL} and password ${process.env.TEST_PASSWORD}`,
);
Using JavaScript string interpolation (${}) bakes the runtime value directly into the instruction and therefore into the cache key. If the value changes (e.g. between staging and production, or between test users), each value gets its own cache entry — cache size grows unboundedly and you lose the benefits of caching.
Declaring accessible variables
Only environment variables explicitly listed in envVars are accessible to the AI. This is a security boundary: it prevents the AI from accidentally reading sensitive environment variables that are present in the process but not intended to be used.
// Only TEST_EMAIL and TEST_PASSWORD are accessible — other env vars are not
await page.ai('Log in as {{$.env.TEST_EMAIL}}', {
envVars: ['TEST_EMAIL', 'TEST_PASSWORD'],
});
Variables referenced in the instruction using {{$.env.VAR_NAME}} syntax are automatically extracted and added to the accessible set — you do not need to list them in envVars separately if they are already present in the instruction string. Listing them explicitly in envVars is useful when the variable is needed by the AI but is not mentioned in the instruction text.
Common use cases
Credentials
await page.ai(
'Log in with username {{$.env.TEST_USERNAME}} and password {{$.env.TEST_PASSWORD}}',
{ envVars: ['TEST_USERNAME', 'TEST_PASSWORD'] },
);
Environment-specific hostnames
await page.ai(
'Navigate to the admin panel at {{$.env.ADMIN_BASE_URL}} and verify the dashboard loads',
{ envVars: ['ADMIN_BASE_URL'] },
);
Test data identifiers
await page.ai(
'Search for order number {{$.env.TEST_ORDER_ID}} and open its detail page',
{ envVars: ['TEST_ORDER_ID'] },
);
How variables are set
Set the variables as normal environment variables before running your tests:
# .env file (loaded by your shell or dotenv)
TEST_EMAIL=test@example.com
TEST_PASSWORD=supersecret
# Or inline for a single run
TEST_EMAIL=test@example.com npx donobu test
In CI, use repository secrets — see CI/CD Integration.