> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> What the sandbox and production share, what differs, where environment variables live, and how to keep a test conversation isolated

Every agent has two environments: the sandbox, where you exercise your local code against the real platform, and production, which serves end users on every channel. They exist so that a change can be tried end to end, model included, before anyone else sees it.

## How the sandbox runs your code

`lua chat` talks to the sandbox unless you pass `-e production`. Before it sends your first message it compiles the project, uploads the compiled skills, preprocessors, and postprocessors as sandbox versions, and attaches the persona from your local code. The platform then runs the conversation with those pieces in place of the deployed ones, so you are talking to your working copy without a `lua push`. Nothing about the sandbox changes what end users see, and the uploaded sandbox versions expire after a day.

Only those three kinds are replaced. Webhooks, jobs, triggers, workflows, MCP servers, knowledge, features, and channels are whatever is live on the agent. To try a webhook or job before it is deployed, use `lua test`, which runs one tool, webhook, or job on your machine with the input you give it, no model involved; the platform APIs it calls are real. `lua test` applies none of the platform's time budgets or payload caps to a tool, webhook, job, or processor: the handler runs until it returns, so a tool that takes 200 seconds passes locally and is stopped at 180 in production; only `lua test workflow` stops a step at its `timeoutSeconds`.

With `-e production` the CLI compiles nothing and uploads nothing; your message goes to the live agent.

## What the two environments share

|                                                       | Sandbox                                              | Production                  |
| ----------------------------------------------------- | ---------------------------------------------------- | --------------------------- |
| Agent ID, knowledge, features, channels, integrations | Shared                                               | Shared                      |
| End-user records (`User`) and `Data` collections      | Shared                                               | Shared                      |
| Skill and processor code                              | Your local code, uploaded at each `lua chat` start   | The deployed versions       |
| Persona                                               | Your local code                                      | The live persona version    |
| Environment variables read by code                    | The `.env` values uploaded with each sandbox version | The agent's server-side map |

<Warning>
  A sandbox conversation reads and writes the same `Data` collections and `User` records as production. Use test identities and a dedicated thread, and don't point a sandbox run at real end-user data.
</Warning>

## Environment variables

Code reads configuration with `env('KEY')` or `process.env.KEY`; both work locally and deployed. Where the value comes from depends on where the code runs.

`lua env production -k KEY -v VALUE` stores a value in the agent's server-side map. Deployed code reads only that map; the platform never passes its own environment through. `lua env production --list` prints the keys with masked values, and `lua env production -k KEY --delete` removes one.

`lua env sandbox -k KEY -v VALUE` writes `.env` only (the server API is used for production); `lua env staging` is accepted as an alias of `sandbox`. `.env` is what `lua test` reads: it loads your shell environment, then `.env` on top, so a `.env` value wins. `lua chat -e sandbox` uploads the `.env` values to the platform with each sandbox version, and sandbox turns read them from there; the values live with that version and expire with it after 24 hours. The command rewrites the file from its key-value pairs, so comments you added by hand are lost.

```ts src/skills/tools/CreateTicketTool.ts theme={null}
import { LuaTool, env } from 'lua-cli';
import { z } from 'zod';

export default class CreateTicketTool implements LuaTool {
  name = 'create_ticket';
  description = 'Open a support ticket in the helpdesk.';
  inputSchema = z.object({ summary: z.string() });

  async execute(input: z.infer<typeof this.inputSchema>) {
    const key = env('HELPDESK_API_KEY');
    if (!key) throw new Error('HELPDESK_API_KEY is not set');
    const res = await fetch('https://helpdesk.example.com/tickets', {
      method: 'POST',
      headers: { Authorization: `Bearer ${key}` },
      body: JSON.stringify(input),
    });
    return await res.json();
  }
}
```

Keep secrets in the environment, never in code or in `Data`. `lua.skill.yaml` holds no environment variables.

## Threads

A thread is one isolated conversation. Without `-t`, `lua chat` continues your default thread with the agent, so earlier test messages shape later answers. `lua chat -t` starts a fresh thread and prints its ID; `lua chat -t <id>` reuses one. `--clear` wipes the thread (or, without `-t`, your whole history with the agent) when you exit, and `lua chat clear` does the same on demand.

## The sandbox and a staged agent version

The sandbox answers "does my local code work?". A staged [agent version](/concepts/releases-and-versions) answers "does what I pushed work, exactly as it will go live?". `lua chat --agent-version <n> -m "…"` previews a version that has been created but not promoted, in production, in a thread of its own; it refuses `-e sandbox`, because sandbox overrides would replace the version's contents. Use the sandbox while you write, the version preview before you promote, and `lua chat -e production` after.

## When to use which

* Iterating on a tool, a skill's `context`, or the persona: `lua chat -m "…" -t`.
* Checking one tool's `execute` with exact input, or a webhook or job: `lua test <type> --name <name> --input '<json>'`.
* Verifying a release candidate: `lua version create`, then `lua chat --agent-version <n> -m "…"`.
* Confirming a live change, or a production environment variable: `lua chat -e production -m "…" -t`.

## Limits

* Sandbox versions are kept for 24 hours; `lua chat` uploads fresh ones at every start.
* `--agent-version` works only against production.
* `lua env` with `-k`, `-v`, `-d`, or `--list` but no environment argument is a usage error; bare `lua env` opens an interactive picker, so scripts must name the environment.
* `lua env production --list` masks each value: the first four characters, then asterisks; values of four characters or fewer are fully masked.

## Next steps

<Columns cols={2}>
  <Card title="Test an agent before you release" href="/ship/testing">`lua test`, sandbox chat, thread isolation, and workflow fixtures.</Card>
  <Card title="Call your API" href="/build/call-your-api">Store a key and read it from a tool.</Card>
  <Card title="lua env" href="/reference/cli/env">Every flag, the masking rule, and the alias list.</Card>
  <Card title="lua chat" href="/reference/cli/chat">Environments, threads, batching, and version preview.</Card>
</Columns>
