Mechanisms you can verify
Credentials on your machine
lua auth configure --email creates a user session: a renewable sign-in stored under ~/.lua-cli/sessions/ with file mode 0600 inside a 0700 directory. Signing out of the admin dashboard, desktop, or mobile app also ends CLI sessions; lua auth logout revokes the session on the server and deletes the local file. An API key saved with lua auth configure --api-key is stored at ~/.lua-cli/credentials, also mode 0600. A command resolves its credential in this order: LUA_API_KEY in the environment or .env, then a stored user session, then the credentials file. lua auth sessions lists your sessions; see About credentials.
API key classes
A legacy key carries its owner’s full access. A scoped key has the formapi_<uuid>.<secret> and carries named scopes, such as agents:read, workflows:execute, or knowledge:write; the REST API checks the scope on each protected route, so a key minted for CI with agents:write and automations:write cannot list end-user conversations (conversations:read) or read environment variables and Data (knowledge:read). Execution logs have their own pair: logs:read to read them and logs:manage to configure a log drain. logs:manage is sensitive — it carries destination credentials — so a wildcard grant does not satisfy it and a key holds it only when it was granted by exact name. On the log routes the scope each one used before is still accepted for two releases — knowledge:read on an agent’s logs, automations:read on a skill’s logs, analytics:read on the organization’s logs and their CSV export — and a request authorized by one of those answers with Deprecation: true and a Sunset date. Chat routes check no scope: any valid credential can talk to the agent as its own end user. A device credential has the same form, is issued for one agent, one device name, and a set of operations, and is the only credential a device client needs.
Secrets in the agent environment
Secrets are stored per environment withlua env production -k <KEY> -v <value> and read in code with env('KEY'); the runtime injects the environment into each invocation, and lua env production --list prints keys with masked values. Local runs read .env, which lua env sandbox writes. Nothing else in your project holds secrets: MCP server headers and URLs you declare with LuaMCPServer resolve through env() at request time, governance in API mode reads GOVERNANCE_API_KEY, and integration credentials stay on the platform, so Integrations.passthrough calls the provider for you and your code never sees a token. The one exception is a webhook’s signing secret, a string literal in source, which travels with every source backup and with your repository.
Custom MCP servers added from the Tools page or the admin dashboard keep their credentials on the platform, encrypted at rest: the server’s address, its header values, its OAuth tokens, and the client secret of your own OAuth app. The address counts as a credential because some servers put a token in it. All of them are write-only: once saved, the address is shown only as its host, followed by /… when it has a path (the CLI shows the host alone), headers only by name, and agent code can’t read any of them. A custom server must be https://, and its address is refused when it is, or resolves to, a private, internal, or cloud-metadata address, both when the server is added and every time Lua connects to it.
Secret scrubbing in logs
Three redactions apply, each to its own surface. Workflow and job records (step failures, provider errors, job output, run events) pass through a scrubber that replaces recognizable credential shapes (well-known provider tokens, JWTs (signed sign-in tokens), private-key blocks, tokens inside git URLs) and anyname=value pair whose name ends in a secret-like word (Authorization, x-api-key, *_KEY, *_TOKEN, password) with [REDACTED]; workflow signal payload keys such as token and api_key are stored redacted. The runtime’s own log lines redact bearer headers, credential query parameters, and JWTs. Passthrough errors have the connection’s credential removed. Nothing redacts what your code prints: console.log output is stored as written, up to 256 KB per field, so never log a secret.
A fourth redaction applies on the way out of the platform. Every record a log drain delivers — body and every string attribute — passes a scrubber first, with no way to disable it: bearer and basic headers, JWTs, private-key blocks, connection-string passwords, URL secret parameters, Lua API keys and handoff codes, and well-known vendor keys (Stripe, Slack, GitHub, Twilio, Google, OpenAI, Anthropic) are replaced with a marker such as [redacted:vendor-key]. An organization can add up to 20 patterns of its own. A drain’s own credentials are write-only: reading a drain returns header names and the last four characters of each value, never a value, and the HMAC signing secret is shown once and can only be rotated, never read back. The delivery log has no field that can hold a request body.
What agent code can reach
Tools, webhooks, jobs, processors, and workflow code steps run in a sandbox created per invocation; module-level state does not persist between calls. Outbound HTTP is allowed. Requests from workflow code steps to private network ranges (private, link-local, carrier-grade NAT, and cloud metadata addresses) are refused with the error codeEGRESS_DENIED. The optional browser feature takes an allowedDomains list that limits where a browser session may navigate. See About execution contexts.
Log drain deliveries are outbound in the other direction, and they leave from the platform’s own network rather than from an agent sandbox — so a receiver behind an IP allow-list needs the egress ranges, not a DNS name. Those ranges are not published yet; Egress allow-listing says where they will be. A drain endpoint is refused at creation unless it is HTTPS and resolves to a public address: private, loopback, link-local, and cloud-metadata addresses are rejected, and a redirect is never followed.
Production change control
lua push uploads a version and changes nothing for end users; a pushed persona is staged until lua deploy persona activates it. lua version create snapshots every primitive’s version into a numbered agent version, and lua version promote <n> makes that snapshot live in one step; promoting an earlier number is the rollback. lua version list --json shows which version is active, who created each version (createdBy), and when it was promoted (promotedAt); nothing records who promoted it, but with lua git connect every promote is also an audit commit in your repository. lua sync --check reports drift between the server and your source, and lua source list keeps the history of pushed source. The Claude Code plugin adds a gate for AI-assisted work: production-affecting commands are denied unless you confirm once. See Releasing.
Human approval for consequential actions
Two mechanisms put a person in the path. GovernancerequireToolApproval pauses a tool call in a conversation until someone approves. A workflow approval step pauses a run with an approver rule (the run’s creator, organization admins, named members, a role, or a group), an optional four-eyes rule (a second person decides; the one who started the run can’t), and a timeout with escalation; approvals are answered in the desktop app or with lua workflows approve.
Telemetry
The CLI sends usage events to an analytics service: the command, whether it succeeded, its duration, the first 200 characters of any error, the operating system, Node and CLI versions, and the organization and agent IDs, keyed by a hash of the API key or a random machine identifier.lua telemetry off or LUA_TELEMETRY=false turns it off; lua telemetry status shows the setting. lua evals opens the evaluations dashboard with your API key in the URL query string; treat that browser history entry as a credential.
Retention
Workflow run records are kept for 90 days by default; an organization administrator can shorten the window to as few as 7 days. Trigger executions are kept for 90 days. Execution logs — everythinglua logs reads — are kept for 90 days by default and then deleted; to hold them longer, copy them out with a log drain. A drain’s delivery records are kept for 7 days.
Security controls and compliance statements
Everything in the previous section is a mechanism: a file mode, a scope check, a redaction, a command that refuses. A compliance statement (a certification, a data-processing agreement, a residency commitment, an uptime figure) is a contractual claim that documentation cannot verify from behavior, so this page makes none. Not stated in these docs, because no verifiable mechanism was found for them:- Encryption at rest for environment variables and stored data.
- Data residency.
- Retention for conversations, channel deliveries, and voice transcripts.
- Published egress IP ranges for log-drain deliveries.
- What the model providers do with the data they receive.
- Audit-log export.
- Rate limits on chat, channel, data, and commerce routes (REST API overview).
When to use it
- Automating from CI: mint a scoped key with only the scopes the job needs and pass it as
LUA_API_KEY. - A tool must never run, or must be confirmed by a person: use governance rules.
- A multi-step workflow touches money or customer data: put an approval step in it.
- Logs have to live in your own SIEM, or longer than 90 days: configure a log drain, and verify its signatures.
- Answering a vendor questionnaire: cite the mechanisms here and put the “Not stated” list to Lua as questions.
Limits
Next steps
About credentials
User sessions, legacy and scoped keys, device credentials, and the scope table.
About governance
Block or gate tool calls and scan for prompt injection.
Releasing
Push, snapshot, promote, and roll back.
About execution contexts
What each kind of code can see and how long it may run.

