Skip to main content

Overview

ExuluVariables provides secure, centralized storage and retrieval of sensitive values like API keys, authentication tokens, and credentials. Variables are stored encrypted in the database and can be referenced across agents, tools, and embedders without exposing the actual values in code or environment files.

Key features

Encrypted storage

Variables are encrypted at rest using AES encryption

Centralized management

Store once, use everywhere across your application

UI/API management

Create and update variables via UI or API

Reference by name

Use variable names instead of hardcoded values

Automatic decryption

Variables are decrypted automatically when retrieved

Reusable

Share variables across agents, tools, and embedders

Why use ExuluVariables?

Secrets arenโ€™t hardcoded in your codebase or stored in .env files that could be committed to version control. Variables are encrypted at rest and only decrypted when retrieved.
Update an API key once, and all agents, tools, and embedders using that variable automatically use the new value. No need to update multiple configuration files.
Manage secrets through the UI or API without redeploying your application. Change API keys, tokens, or credentials on the fly.
Use different variables for development, staging, and production environments while keeping the same codebase.
Track who created or updated variables (when integrated with your audit system).

Quick start

Storing a variable

Variables can be created via the UI or API:
// Via API or database
INSERT INTO variables (name, value, encrypted)
VALUES ('openai_api_key', 'encrypted_value_here', true);

Retrieving a variable

import { ExuluVariables } from "@exulu/backend";

// Get a variable value
const apiKey = await ExuluVariables.get("openai_api_key");

console.log(apiKey); // "sk-..."
The variable is automatically decrypted if it was stored encrypted.

Common use cases

API keys for LLM providers

Store API keys for OpenAI, Anthropic, Google, and other providers:
import { ExuluVariables } from "@exulu/backend";

// Store once in database:
// name: "openai_api_key", value: "sk-...", encrypted: true
// name: "anthropic_api_key", value: "sk-ant-...", encrypted: true

// Use in agents
const openaiKey = await ExuluVariables.get("openai_api_key");

const agent = new ExuluAgent({
  id: "assistant",
  name: "OpenAI Assistant",
  type: "agent",
  description: "AI assistant",
  provider: "openai",
  authenticationInformation: openaiKey, // Use retrieved variable
  config: {
    name: "gpt-4o",
    model: {
      create: ({ apiKey }) => createOpenAI({ apiKey: apiKey || openaiKey })("gpt-4o")
    },
    instructions: "You are a helpful assistant."
  },
  capabilities: { text: true, images: [], files: [], audio: [], video: [] }
});

Embedding API keys

Use variables for embedding provider credentials:
import { ExuluVariables, ExuluEmbedder } from "@exulu/backend";

// Retrieve the API key
const openaiKey = await ExuluVariables.get("openai_api_key");

// Use in embedder
const embedder = new ExuluEmbedder({
  id: "openai_embedder",
  name: "OpenAI Embeddings",
  provider: "openai",
  model: "text-embedding-3-small",
  vectorDimensions: 1536,
  authenticationInformation: openaiKey // Use retrieved variable
});

Tool authentication

Store credentials for external services used by tools:
import { ExuluVariables, ExuluTool } from "@exulu/backend";
import { z } from "zod";

// Create tool with variable reference
const githubTool = new ExuluTool({
  id: "github_search",
  name: "search_github",
  description: "Searches GitHub repositories",
  type: "function",
  inputSchema: z.object({
    query: z.string()
  }),
  config: [
    {
      name: "github_token",
      type: "variable", // Mark as variable reference
      value: "github_api_token" // Variable name
    }
  ],
  execute: async ({ query }, config) => {
    // Retrieve the variable inside execute
    const token = await ExuluVariables.get("github_api_token");

    const response = await fetch(
      `https://api.github.com/search/repositories?q=${query}`,
      {
        headers: {
          Authorization: `Bearer ${token}`
        }
      }
    );

    const data = await response.json();
    return { result: JSON.stringify(data.items.slice(0, 5)) };
  }
});

Database credentials

Store database connection strings and credentials:
import { ExuluVariables } from "@exulu/backend";

// Retrieve database URL
const databaseUrl = await ExuluVariables.get("database_url");

// Use in database connection
const db = createDatabaseClient({
  connectionString: databaseUrl
});

Multi-tenant applications

Different tenants can use different API keys:
import { ExuluVariables } from "@exulu/backend";

// Store tenant-specific keys:
// name: "tenant_1_openai_key", value: "sk-...", encrypted: true
// name: "tenant_2_openai_key", value: "sk-...", encrypted: true

async function getAgentForTenant(tenantId: string) {
  const variableName = `tenant_${tenantId}_openai_key`;
  const apiKey = await ExuluVariables.get(variableName);

  return new ExuluAgent({
    id: `tenant_${tenantId}_agent`,
    name: `Agent for Tenant ${tenantId}`,
    type: "agent",
    description: "Tenant-specific agent",
    provider: "openai",
    authenticationInformation: apiKey,
    config: { /* ... */ },
    capabilities: { text: true, images: [], files: [], audio: [], video: [] }
  });
}

How it works

Storage

Variables are stored in the variables table in PostgreSQL:
CREATE TABLE variables (
  name VARCHAR PRIMARY KEY,
  value TEXT NOT NULL,
  encrypted BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Encryption

When encrypted: true, variables are encrypted using AES encryption with NEXTAUTH_SECRET as the encryption key:
// Encryption (when storing)
const encrypted = CryptoJS.AES.encrypt(value, process.env.NEXTAUTH_SECRET).toString();

// Decryption (when retrieving)
const bytes = CryptoJS.AES.decrypt(encrypted, process.env.NEXTAUTH_SECRET);
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
The NEXTAUTH_SECRET environment variable must be set and consistent across deployments. If it changes, previously encrypted variables cannot be decrypted.

Retrieval

When you call ExuluVariables.get():
  1. Queries the variables table for the specified name
  2. Throws an error if the variable doesnโ€™t exist
  3. Decrypts the value if encrypted: true
  4. Returns the decrypted value

Variable workflow

1

Create variable

Store a variable in the database via UI or API
// Via UI: Navigate to Settings โ†’ Variables โ†’ Add Variable
// Or via API/SQL:
INSERT INTO variables (name, value, encrypted)
VALUES ('my_api_key', 'sk-...', true);
2

Reference in code

Retrieve the variable using ExuluVariables.get()
const apiKey = await ExuluVariables.get("my_api_key");
3

Use in configuration

Pass the retrieved value to agents, tools, or embedders
const agent = new ExuluAgent({
  // ...
  authenticationInformation: apiKey
});
4

Update as needed

Update the variable value through UI or API without code changes
UPDATE variables
SET value = 'new_encrypted_value'
WHERE name = 'my_api_key';

Variable naming conventions

Use descriptive, namespaced names for variables to avoid conflicts and improve clarity.
Recommended patterns:
// Provider API keys
"openai_api_key"
"anthropic_api_key"
"google_api_key"

// Service-specific
"github_api_token"
"stripe_api_key"
"sendgrid_api_key"

// Environment-specific
"prod_openai_api_key"
"dev_openai_api_key"

// Tenant-specific
"tenant_123_openai_key"
"tenant_456_anthropic_key"

// Feature-specific
"embeddings_openai_key"
"chat_openai_key"

Error handling

import { ExuluVariables } from "@exulu/backend";

try {
  const apiKey = await ExuluVariables.get("my_variable");
  console.log("Retrieved variable:", apiKey);
} catch (error) {
  console.error("Variable not found:", error.message);
  // Handle missing variable (use default, prompt user, etc.)
}
ExuluVariables.get() throws an error if:
  • The variable name doesnโ€™t exist in the database
  • Database connection fails
  • Decryption fails (e.g., wrong NEXTAUTH_SECRET)

Security considerations

Protect your NEXTAUTH_SECRET: This key is used to encrypt/decrypt variables. Store it securely and never commit it to version control.
Database access: Variables are stored in your PostgreSQL database. Ensure proper database access controls and backups.
Encryption at rest: Variables with encrypted: true are encrypted in the database. They are decrypted in memory when retrieved.
Principle of least privilege: Only grant access to variables that are necessary. Consider using separate variables for different environments or tenants.

Comparison with .env files

FeatureExuluVariables.env files
StorageDatabase (encrypted)File system (plain text)
ManagementUI/APIManual file editing
UpdatesNo redeployment neededRequires restart/redeploy
SharingCentralized across appPer-instance
Version controlNot in codeRisk of accidental commit
Encryptionโœ… Built-inโŒ Plain text
Audit trailโœ… PossibleโŒ No
Multi-tenantโœ… EasyโŒ Difficult
Use ExuluVariables when:
  • You need to update secrets without redeployment
  • Multiple agents/tools share the same credentials
  • You want encrypted storage
  • You need centralized management
  • Youโ€™re building a multi-tenant application
Use .env files when:
  • You have simple, static configuration
  • Secrets are environment-specific and rarely change
  • You prefer file-based configuration

Integration examples

Complete agent setup

import { ExuluAgent, ExuluVariables } from "@exulu/backend";
import { createOpenAI } from "@ai-sdk/openai";

// Retrieve API key
const openaiKey = await ExuluVariables.get("openai_api_key");

// Create agent
const agent = new ExuluAgent({
  id: "customer_support",
  name: "Customer Support Agent",
  type: "agent",
  description: "Handles customer inquiries",
  provider: "openai",
  authenticationInformation: openaiKey,
  config: {
    name: "gpt-4o",
    model: {
      create: ({ apiKey }) => createOpenAI({ apiKey: apiKey || openaiKey })("gpt-4o")
    },
    instructions: "You are a helpful customer support agent."
  },
  capabilities: { text: true, images: [], files: [], audio: [], video: [] }
});

// Use agent
const response = await agent.generateSync({
  prompt: "How do I reset my password?",
  agentInstance: await loadAgent("customer_support"),
  statistics: { label: "support", trigger: "api" }
});

console.log(response);

Multi-provider setup

import { ExuluAgent, ExuluVariables } from "@exulu/backend";

// Retrieve keys for multiple providers
const [openaiKey, anthropicKey, googleKey] = await Promise.all([
  ExuluVariables.get("openai_api_key"),
  ExuluVariables.get("anthropic_api_key"),
  ExuluVariables.get("google_api_key")
]);

// Create agents for each provider
const openaiAgent = new ExuluAgent({
  id: "openai_agent",
  provider: "openai",
  authenticationInformation: openaiKey,
  // ... config
});

const anthropicAgent = new ExuluAgent({
  id: "anthropic_agent",
  provider: "anthropic",
  authenticationInformation: anthropicKey,
  // ... config
});

const googleAgent = new ExuluAgent({
  id: "google_agent",
  provider: "google",
  authenticationInformation: googleKey,
  // ... config
});

Best practices

Always encrypt sensitive values: Set encrypted: true for API keys, tokens, passwords, and other secrets.
Use descriptive names: Name variables clearly to avoid confusion (e.g., openai_api_key instead of key1).
Handle missing variables: Always wrap ExuluVariables.get() in try/catch to handle cases where variables donโ€™t exist.
Document your variables: Maintain a list of required variables for your application, especially when onboarding new team members.

Next steps