Configuration#
The Lens Prism runtime is configured entirely through environment variables. For a native install they go in ~/.prism/.env (restart the service after editing); for Docker, pass them with -e. Only an LLM credential is required — everything else has a sensible default.
Provider selection#
The runtime talks to one LLM provider per container, chosen by LLM_PROVIDER.
| Variable | Description | Default |
|---|---|---|
LLM_PROVIDER |
LLM platform: bedrock (AWS Bedrock runtime), bedrock-mantle (AWS Bedrock Mantle endpoint), or azure (Claude via Microsoft Foundry). Any other value falls back to bedrock. |
bedrock |
All three serve Claude through the Anthropic Messages API. azure mode runs the agent on Claude deployed in Microsoft Foundry (https://{resource}.services.ai.azure.com/anthropic/v1/messages). On Azure, memory embeddings come from an Azure OpenAI text-embedding-3 deployment (Anthropic has no embedding model), defaulting to text-embedding-3-small; if that deployment doesn't exist, memory embedding fails open (search degraded, no crash).
bedrock-mantle targets AWS's Bedrock Mantle inference endpoint, a second endpoint separate from the Bedrock runtime. It reaches models the Bedrock runtime does not and draws on a separate quota pool. Like azure, it speaks the Anthropic Messages API surface. Direct mode authenticates with AWS_BEARER_TOKEN_BEDROCK (sent as the x-api-key header); routed through Nexus, the gateway holds it. Mantle serves chat only — memory embeddings still route through the Bedrock runtime.
Each platform can run direct or through Nexus (the LLM gateway). Routing is automatic: set NEXUS_API_URL and the runtime routes through Nexus, otherwise it talks to the platform directly. Through Nexus the gateway holds the platform credentials, so no local LLM credential is needed.
Credentials#
| Variable | Description |
|---|---|
AWS_BEARER_TOKEN_BEDROCK |
Bedrock API key. The AI SDK auto-selects bearer auth when this is set. This is the one credential required for chat on Bedrock, and the only one on bedrock-mantle. |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
Bedrock AWS SigV4 credentials, as an alternative to the bearer token on the Bedrock runtime. |
AZURE_API_KEY |
Azure Foundry resource key, sent as the x-api-key header. Required for direct mode only — when routing through Nexus the gateway holds the key for both chat and embeddings. May be a __nexus_cred:<uuid>__ sentinel the proxy rewrites on the wire. |
AZURE_RESOURCE_NAME |
Azure (direct) Foundry resource name — derives the chat endpoint https://{resource}.services.ai.azure.com/anthropic/v1 and the embeddings endpoint https://{resource}.openai.azure.com. |
AZURE_FOUNDRY_BASE_URL |
Azure. Override the derived Foundry Anthropic chat endpoint (must end at /anthropic/v1). |
SLACK_BOT_TOKEN |
Slack bot token (xoxb-…). Enables the Slack adapter; validated via auth.test on boot. |
SLACK_APP_TOKEN |
Slack app-level token (xapp-…) for Socket Mode. |
SLACK_EXPECTED_APP_ID |
Optional safety check — the bot token's app ID must match this if set. |
See Slack for how the tokens are used. Bedrock is not mandatory — set the credentials for whichever provider LLM_PROVIDER selects.
Core#
| Variable | Description | Default |
|---|---|---|
PORT |
HTTP port the runtime listens on. | 3003 |
DATABASE_URL |
SQLite URL for the agent database. The Docker image's start.sh sets this to file:/data/agent.db; locally it resolves next to the Prisma schema. |
file:/data/agent.db (Docker) |
AGENT_ID |
Pin the agent's UUID. Unset generates a fresh UUID on first boot and reuses it thereafter. | generated |
AGENT_NAME |
Display name on first boot. | Prism |
TEAM_NAME |
Team name on first boot. | Local |
OWNER_USER_ID |
Owner user ID on first boot. | local sentinel UUID |
SHELL_WORKSPACE_ROOT |
Working directory for the agent's shell tools. | install workspace dir (Docker: /home/node) |
PRISM_DATA_DIR |
Persistent data dir for skills, plugins, and the Claude Code config. The Docker image sets this to the /data volume. |
/data (Docker), else ~/.prism |
PRISM_SKILLS_DIR |
Override the skills directory specifically. | <data dir>/skills |
PRISM_PLUGINS_DIR |
Override the plugins directory specifically. | <data dir>/plugins |
AGENT_NAME, TEAM_NAME, and OWNER_USER_ID only matter on the first boot with an empty database; later boots reuse the stored workspace.
Webhooks need no environment variables — the receiver toggle and subscriptions live in the database, managed from the web UI. The runtime doesn't authenticate inbound webhook calls itself (auth is the platform's job in front of the container), so don't expose the runtime port directly to the internet.
Chat verbosity#
The verbose setting controls how multi-step chat turns are delivered.
| Mode | Behavior |
|---|---|
Concise (default, verbose=false) |
Only the final text step of a turn is delivered to the channel. Intermediate steps are still saved but marked internal, so they stay hidden both live and when the thread is reloaded. The final message must be self-contained. |
Verbose (verbose=true) |
Every text step is delivered as it arrives — the streaming behavior. |
This is a per-agent database setting, not an environment variable. Set it via the Profile page toggle in the web UI, PATCH /agents/verbose with { "verbose": true | false }, or by asking the agent (the update_settings tool accepts verbose).
Persistence is unchanged — every step is always stored, and concise mode only flags the intermediate ones as internal — so the model's context window is correct regardless of mode.
Locked-in mode#
The lockedIn setting stops the agent from rewriting its own identity documents. While the lock is on, the agent's document-writing and memory-writing tools are withheld and any such write is refused, so the agent keeps running but cannot reshape who it is or add to what it knows.
| Frozen while locked | Editable while locked |
|---|---|
Soul, User, Memory (the curated summary), Vision, Heartbeat config, Bootstrap, and searchable memory entries (memory_write, memory_delete) |
Behavioral settings (heartbeat interval, timezone, verbose), and the agent's name (rename_self) |
Searchable memory stays readable: memory_search is available while the lock is on.
The Agent Guide is in neither column — no tool writes it, so it is read-only to the agent whether the lock is on or off.
Turning the lock on takes effect immediately. A locked agent asked to change one of the frozen documents says what it would have changed and points to this setting. Onboarding is paused while the lock is on: a locked agent with a non-empty Bootstrap document does not run the first-run script, because it could not clear it afterward. The document is kept, so onboarding runs once you unlock the agent.
An agent cannot unlock itself. Set the lock two ways:
- The Locked-in mode toggle on the Profile page in the web UI.
PATCH /agents/locked-inwith{ "lockedIn": true | false }.
Warning
Locked-in mode is a guardrail against drift, not a security boundary. The shell is never withheld, so an agent with shell access can write the documents straight into the database or reach the unlock endpoint on the local runtime; POST /agents/import is also ungated and can stage a replacement database. Treat the lock as protection against accidental self-modification, not against a determined agent.
Models#
Which set applies depends on LLM_PROVIDER. Bedrock model IDs are Bedrock model identifiers; Azure model IDs are Foundry deployment names (which default to Claude model IDs when you deploy without renaming).
bedrock-mantle reuses the same BEDROCK_* variables, but its model IDs must be bare — no us. cross-region inference-profile prefix and no version suffix (for example anthropic.claude-opus-5 rather than us.anthropic.claude-opus-5). The BEDROCK_MODEL_ID default below carries the prefix, so set it explicitly on Mantle. Embedding IDs are unaffected: embeddings always run through the Bedrock runtime.
| Variable | Description | Default |
|---|---|---|
BEDROCK_MODEL_ID |
Bedrock main model (the agent's reasoning loop). | us.anthropic.claude-opus-5 |
BEDROCK_CLASSIFICATION_MODEL_ID |
Bedrock fast model for the heartbeat DELIVER/SUPPRESS gate. | a Claude Haiku model |
BEDROCK_EMBEDDING_MODEL_ID |
Bedrock embedding model for searchable memory. | amazon.titan-embed-text-v2:0 |
AWS_REGION |
Bedrock AWS region. | us-east-1 |
AZURE_MODEL_ID |
Azure main model — a Claude chat deployment name. | claude-opus-5 |
AZURE_CLASSIFICATION_MODEL_ID |
Azure fast Claude deployment for the heartbeat gate. | claude-haiku-4-5 |
AZURE_EMBEDDING_MODEL_ID |
Azure OpenAI embedding deployment (a text-embedding-3 model; truncated to 1024 dims to match the memory index). The deployment must exist on the resource, else memory embedding fails open. Set to '' to skip embeddings entirely. |
text-embedding-3-small |
AZURE_API_VERSION |
Azure OpenAI API version (embeddings only). | SDK default (preview) |
CONTEXT_WINDOW_TOKENS |
Input-context window (tokens) to assume for the agent's model. Only needed when the catalog doesn't recognize it. Ignored unless a positive integer. | derived from the model ID |
MODELS_DEV_URL |
Refresh the model-window catalog from a registry once at startup. Unset means no outbound call, bundled catalog only. | disabled |
The main model can also be set per-agent at runtime (web UI Profile, the TUI /model command, or by asking the agent), which overrides the env default. On Azure, per-agent overrides are Foundry deployment names.
Heartbeat and scheduled (cron) runs can each use their own model, set the same ways — the web UI Profile page or the TUI /heartbeat-model and /cron-model commands. Both default to empty and fall back to the main model (which falls back to BEDROCK_MODEL_ID), so you can run routine heartbeats on a cheaper model while chat stays on a stronger one. The heartbeat gate always uses the active platform's classification model, independent of this.
Context-window size#
The web UI and terminal UI show how much of the model's context window a conversation has used. The runtime reads it from a bundled catalog of Claude window sizes sourced from models.dev. That bundled copy needs no network and is the only source by default.
Set MODELS_DEV_URL=https://models.dev/api.json to also refresh the catalog once at startup, so a model released after the build gets a window. Enable it deliberately — models.dev is community-maintained, so a wrong number upstream reaches your deployment without review.
Warning
In a sandboxed or egress-filtered deployment, enabling MODELS_DEV_URL means allow-listing models.dev — and an allow-list is not per-process. Once the domain is reachable, it's reachable by the agent's own tools too, not just the startup refresh. Treat it as adding a domain of untrusted, community-edited content to the web the agent can reach and act on.
When a model is in neither layer the indicator is hidden rather than guessed. That's most likely on Azure, where model IDs are deployment names you chose: AZURE_MODEL_ID=my-opus-deploy can't appear in any catalog. Set CONTEXT_WINDOW_TOKENS to that model's input-context size (for example 1000000) to bring it back. It applies to every model the agent uses, so the main and heartbeat models can't be given different windows.
Conversation compaction#
As a thread grows, the runtime keeps it within the model's context window by compacting older history rather than dropping it. After a turn that belongs to a stored thread, once context occupancy reaches COMPACTION_THRESHOLD_PERCENT of the window, the runtime summarizes older messages and keeps the newest COMPACTION_TAIL_MESSAGES verbatim. Each thread carries a single summary: a new compaction folds the previous summary into its replacement, so cost stays bounded. Slack threads are exempt — they re-read their history live from Slack on each turn.
| Variable | Description | Default |
|---|---|---|
COMPACTION_THRESHOLD_PERCENT |
Context-window occupancy (percent) that triggers compaction after a turn in a stored thread. 0 disables compaction. |
90 |
COMPACTION_TAIL_MESSAGES |
Number of most-recent messages kept verbatim when compacting; older history is summarized. | 6 |
Compaction can also be run on demand with the /compact slash command.
Bootstrap model defaults#
When the runtime first creates an agent (empty database), it seeds the three model overrides from ~/.prism/settings.json if present — the same file the prism TUI reads.
| Key | Seeds |
|---|---|
modelId |
the main model |
heartbeatModelId |
the heartbeat model |
cronModelId |
the scheduled-task model |
Each is optional; an unset, blank, or malformed value leaves that override empty (and it falls back as above). These apply only at creation — editing them later doesn't touch an existing agent; use the Profile page or the TUI commands for that.
Voice meetings#
Voice meetings let the agent join a Google Meet call and take part by voice. The tools appear only when OPENAI_API_KEY is set and the host has the browser and audio toolchain, which ships in the container image.
The voice model is OpenAI's Realtime API — the one model call that does not route through the configured provider or the Nexus gateway (Bedrock, Azure, and Bedrock Mantle serve no realtime voice surface). The socket connects straight to api.openai.com, so an egress-filtered deployment must allow that host, plus Google's meeting and accounts hosts, including WebRTC UDP. The signed-in browser profile is persisted under the data directory, so sign-in happens once rather than per meeting.
| Variable | Purpose | Default |
|---|---|---|
OPENAI_API_KEY |
Enables voice meetings. May be a managed-credential sentinel. | — (disabled) |
OPENAI_REALTIME_MODEL |
Realtime model for the in-meeting voice. gpt-realtime-mini is materially cheaper. |
gpt-realtime-2.1 |
OPENAI_REALTIME_VOICE |
Voice name. | marin |
OPENAI_TRANSCRIPTION_MODEL |
Transcriber used to decide whether the agent was addressed. Runs on every call, billed per minute of audio; never fed to the model. The default is the only streaming model that also accepts name hints. | gpt-live-transcribe |
GOOGLE_BOT_EMAIL |
Google account the agent signs into for meetings. Effectively required — Google auto-denies automated anonymous guests. Use a dedicated account. | — |
GOOGLE_BOT_PASSWORD |
Password for that account. Must be the real password in plain text — a credential sentinel does not work here, because the browser types it into Google's sign-in form rather than sending it as a header. Accounts with a phone or hardware-key challenge, or enforced SSO, cannot be signed in unattended. | — |
MEETING_TIMEOUT_MINUTES |
Hard cap on one call. A non-positive or malformed value falls back to the default. | 60 |
MEETING_ADMISSION_TIMEOUT_SECONDS |
How long to wait in the waiting room for a host to admit the agent, counted from when the join request goes in. | 300 |
Observability#
All optional; each is off until its variable is set. Langfuse traces every LLM call (model, tokens, tool calls, latency); OpenTelemetry exports product telemetry to a collector. Structured logs (Pino) are always on.
| Variable | Description | Default |
|---|---|---|
LANGFUSE_SECRET_KEY |
Enables Langfuse LLM tracing when set. | — |
LANGFUSE_PUBLIC_KEY |
Langfuse public key. | — |
LANGFUSE_BASE_URL |
Langfuse server URL. | http://localhost:3100 |
OTEL_EXPORTER_OTLP_ENDPOINT |
Enables OpenTelemetry export when set. | — |
OTEL_EXPORTER_OTLP_API_KEY |
Collector auth, sent as x-api-key. |
— |
DEPLOYMENT_ENVIRONMENT |
Tags OTel records (production / staging / development). |
production (published images) |
With OpenTelemetry enabled, the runtime emits product telemetry events. They carry only identifiers, flags, and counts — never credentials, URLs, file paths, or message content.
Installer variables#
These affect the native installer, not the running agent.
| Variable | Description | Default |
|---|---|---|
PRISM_HOME |
Install directory. | ~/.prism (%USERPROFILE%\.prism) |
PRISM_HTTP_URL |
Runtime URL the prism TUI connects to. |
http://localhost:3003 |