# Variables

> Declare named read-only runtime values from literals, environment variables, files, or executable providers, then let an agent list, read, or reference them.

A variable is a named, read-only value that an agent can use at runtime. Examples are the current time, a host name, or a token that a command needs. An agent can list a variable’s metadata, read its value, or pass a reference to a tool without ever seeing the value.

Variables are not mutable state, and they are not an encrypted vault.

## A complete example

This config uses no external services. Replace the model placeholder before you send a prompt.

```json
{
  "default_agent": "raw",
  "models": {
    "local": {
      "provider": "ollama",
      "method": "openai-chat-completions",
      "model_id": "YOUR_INSTALLED_MODEL"
    }
  },
  "vars": {
    "project": {
      "description": "Project settings",
      "access": "read",
      "source": { "kind": "literal", "value": { "name": "raw-cli" } }
    },
    "now": {
      "description": "Current UTC time",
      "access": "read",
      "source": { "kind": "provider", "name": "system.time" }
    },
    "token": {
      "description": "API credential",
      "access": "use",
      "source": { "kind": "env", "name": "MY_API_TOKEN" }
    }
  },
  "agents": {
    "raw": {
      "model": "local",
      "vars": ["project", "now", "token"],
      "tools": {
        "use": ["builtin/list_vars", "builtin/read_var", "builtin/bash"]
      }
    }
  }
}
```

Save this as `raw.json`, then run:

```sh
raw --config raw.json vars list
raw --config raw.json vars get now
```

Neither command needs a model credential, and neither starts a session.

## Declare a variable

Variables are declared under the root `vars` object. Names must match `[a-z][a-z0-9_.-]{0,63}`. The names `constructor` and `prototype` are reserved, and `__proto__` is invalid.

| Field          | Required | Meaning                                                                                                                     |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `description`  | Yes      | A nonempty description. The model sees it in `list_vars`.                                                                   |
| `access`       | Yes      | `read` or `use`. See below.                                                                                                 |
| `source`       | Yes      | Where the value comes from. See below.                                                                                      |
| `type`         | No       | `string`, `number`, `boolean`, `object`, `array`, `null`, or `json`. Declared types are checked against the resolved value. |
| `cache_ttl_ms` | No       | Integer from `0` to `2147483647`. Default `0`, which resolves the value on every read.                                      |

### Access

| Access | The model can read the value | The model can pass a reference to Bash or a tool |
| ------ | ---------------------------- | ------------------------------------------------ |
| `read` | Yes                          | Yes                                              |
| `use`  | No                           | Yes                                              |

Use `use` for secrets that a command needs but the model should not see. The `use` setting is not an operating-system secrecy boundary. A command you run can still print the value, and a trusted plugin can read it.

### Sources

Each source has a `kind`, and each kind has its own fields.

| Kind       | Fields                                       | Notes                                                                                                                     |
| ---------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `literal`  | `value`                                      | Any JSON value. The type is inferred unless you declare one.                                                              |
| `env`      | `name`                                       | An environment variable. An unset variable is an error. An empty value is valid.                                          |
| `file`     | `path`, optional `format` (`text` or `json`) | Relative paths resolve from the config file’s directory. The file must be regular, strict UTF-8, and at most 65536 bytes. |
| `provider` | `name`, optional `params`                    | Calls a provider. `system.time` is built in and returns the current UTC time as a string.                                 |

## Select variables for an agent

An agent lists the variables it can use in `vars`, by exact name. There is no wildcard. A variable that is not listed is invisible to that agent.

To let the model discover and read variables, also select `builtin/list_vars` and `builtin/read_var` in `tools.use`. `raw config init` does this for its starter agent, and it also declares a `now` variable.

## Write an executable provider

A provider is a program that answers a single request. Declare it under the root `var_providers` object.

| Field              | Required | Default                     | Notes                                                     |
| ------------------ | -------- | --------------------------- | --------------------------------------------------------- |
| `command`          | Yes      | None                        | An executable name on `PATH`, or a path.                  |
| `args`             | No       | `[]`                        | Literal arguments.                                        |
| `cwd`              | No       | The config file’s directory | Working directory for the program.                        |
| `timeout_ms`       | No       | `5000`                      | From `1` to `2147483647`.                                 |
| `max_output_bytes` | No       | `65536`                     | From `1` to `1048576`. Limits stdout and stderr combined. |

Raw writes one JSON line to the program’s stdin and closes stdin:

```json
{"protocol_version":1,"name":"hostname","params":{"field":"hostname"}}
```

The program must exit `0` and print exactly one JSON object on stdout:

```json
{"value":"workstation","observed_at":"2026-09-26T10:00:00.000Z"}
```

- `value` is required.
- `observed_at` is optional. It must be a UTC timestamp. If it is missing, Raw uses the time the value was produced.
- Extra fields, or a second JSON object on stdout, cause an error.
- Write logs to stderr. Raw counts their bytes but does not return them to the model.

A working example is in [examples/providers/host-info](https://github.com/lploc94/raw-cli/tree/main/examples/providers/host-info). Copy the folder and run the commands from its README.

## Let a model read variables

Once an agent selects `builtin/list_vars` and `builtin/read_var`, the model can call:

- `list_vars` to get each selected variable’s name, description, type, and access. It never runs a provider.
- `read_var` with a `name` to get the value, along with when it was observed and whether it came from the cache.

A `use` variable cannot be read this way. The model can only pass its reference to a command.

## Pass a variable to Bash without showing it

A Bash command can receive a variable as an environment variable, without the value appearing in the command text. Put the mapping in `env_refs` on a command.

```json
{
  "commands": [
    {
      "command": "test -n \"$TOKEN\"",
      "env_refs": { "TOKEN": "token" }
    }
  ]
}
```

Raw resolves the value just before that command starts, and it sets the value only in that command’s environment. The model’s arguments keep the reference, not the value. If a resolution fails, the commands after it do not run, but the commands before it keep their results.

## Use a variable in a custom tool

Every local tool that an agent selects receives `context.vars`. The handler can use these methods:

- `list()` returns metadata only.
- `read(name, { signal })` returns a value.
- `validateEnvRefs(refs)` checks names and types without reading values.
- `resolveEnv(refs, { signal })` returns environment bindings for trusted code.

```js
const refs = { TOKEN: args.token_ref };
context.vars.validateEnvRefs(refs);
const env = await context.vars.resolveEnv(refs, { signal: context.signal });
const response = await fetch(args.url, {
  headers: { Authorization: `Bearer ${env.TOKEN}` },
  signal: context.signal
});
```

MCP servers do not receive variable references. Their `env` and `headers` values are literal.

## Freshness

- The `cache_ttl_ms` value is measured from when a read completes, not from the provider’s timestamp. Only successful reads are cached.
- The cache lives in memory for one runtime. It is not saved to disk and is not shared between sessions.
- The CLI commands `vars list` and `vars get` start with an empty cache every time.
- Each resume reads the current config. A running runtime keeps the definitions it loaded until it restarts.

## Check variables from the command line

```sh
raw [--config PATH] [--agent NAME] vars list
raw [--config PATH] [--agent NAME] vars get NAME
```

- `vars list` prints one JSON object with the metadata of each selected variable.
- `vars get NAME` prints `{"name", "value", "observed_at", "cached"}`.
- `vars get` on a `use` variable fails. Check a `use` variable by running a command that uses it and does not print it.
- Only `--config` and `--agent` apply to these commands.

| Exit code | Meaning                                             |
| --------- | --------------------------------------------------- |
| `0`       | Success.                                            |
| `1`       | The value could not be read, or access was refused. |
| `2`       | Invalid config or arguments.                        |
| `130`     | Cancelled.                                          |

## Troubleshoot a provider

1. Run `raw --config PATH config list` to validate the config. This checks structure and references, and it does not run any provider.
2. Run `vars list` to confirm the variable is selected by the agent.
3. Run the provider program directly with the JSON request on stdin, and confirm that stdout contains only the JSON response.
4. Check the exit code, the value’s type, the timestamp format, and the output size limit.

> **Note:**
>
> Relative paths in `file` sources resolve from the config file’s directory, not the session’s working directory. Set `cwd` on a provider to change where that program runs.

## Related

- [Tools](https://raw.tlelabs.com/docs/extend/tools/) covers the variable tools and the `env_refs` field in Bash.
- [Configuration reference](https://raw.tlelabs.com/docs/reference/configuration/) lists the variable fields.
