Deployment & Security
Aura Workshop ships as a desktop app and a headless daemon image built from the same Rust
crate. This page covers how to run it as a server, reach it from a browser, deploy it to a
remote host, and — most importantly — what its security controls actually defend against.
The single most important fact: isolation strength depends entirely on where the
agent runs. The desktop "Docker mode" only wraps the bash tool; the
daemon container isolates the whole process; guardrails and redaction are defense-in-depth
on top, and several of them ship disabled by default.
Web viewer UI (browser access)
Aura Workshop embeds an axum HTTP server that serves the
SolidJS UI over any browser on port 18800. In the desktop app the same server
can be toggled on to reach the app from another device; the daemon image runs it as the
primary surface. Depending on the mode, the server delivers either the full frontend or a
lighter viewer SPA (set AURA_VIEWER_MODE=true) tuned for
monitoring and driving a deployed agent.
Enabling it on the desktop app
- Open Settings → Connectivity.
- Toggle the Web UI Server on and set the port (default
18800). - Optionally set a Bearer token so requests must authenticate.
- Open
http://localhost:18800— orhttp://<host-ip>:18800from another machine on the LAN.
Serving the UI to anything beyond localhost means it is reachable by anyone who
can route to the port. Configure a token (below) and, for anything past a trusted LAN, put
it behind a TLS reverse proxy.
Docker daemon image
For always-on server use, run the aura-daemon image
(coolkoo/aura-workshop:daemon-latest on Docker Hub). It runs the entire
shared runtime — agent loop, tools, MCP, scheduler, listeners, webhooks, workflows, and the
SQLite DB — inside the container. This is isolation layer G
(below) and the strongest boundary Aura offers.
Single full mode
The daemon runs one mode. --mode is still accepted for backward compatibility:
worker is an alias for full, and any other value is coerced to
full with a log line. There are no inference-master / inference-worker modes and
no LAN GPU cluster — Aura does not run local model inference. To use a local model, run any
OpenAI-compatible server yourself (Ollama, LM Studio, vLLM, LocalAI, …) and add it as a
custom provider in Settings → Providers; Aura
talks to it over HTTP but does not bundle or supervise it.
Run it
docker run -d \
--name aura-daemon \
-p 18800:18800 \
-p 18790:18790 \
-v aura-data:/data \
-e AURA_WEB_TOKEN=$(uuidgen) \
-e AURA_API_KEY=sk-... \
-e AURA_MODEL=deepseek-chat \
-e AURA_BASE_URL=https://api.deepseek.com \
coolkoo/aura-workshop:daemon-latest
The SQLite DB lives at /data/aura-workshop.db (WAL mode) and file memory at
/root/.aura; mount /data on a volume to persist across restarts.
The image is a multi-arch manifest spanning linux/amd64 and
linux/arm64 — Docker pulls the right variant automatically. A built-in
HEALTHCHECK hits /api/health every 30s.
Common environment variables
| Variable | Purpose | Default |
|---|---|---|
AURA_WEB_TOKEN | Bearer token for /api/* and most /acp/* (env wins over the DB setting) | none |
AURA_API_KEY / AURA_MODEL / AURA_BASE_URL | Seed the default provider on first boot | none / deepseek-chat / — |
AURA_VIEWER_MODE | Serve the viewer SPA instead of the full frontend | false |
AURA_REMOTE_DEPLOYMENT | Strict auth — reject anonymous /api/* even with no token set | false |
AURA_HEARTBEAT_URL / AURA_DEPLOYMENT_ID | POST status every 30s + payload UUID | none |
AURA_LICENSE_KEY | License key injected by the deployer | none |
AURA_DB_PATH | SQLite path (daemon image only) | /data/aura-workshop.db |
ACP peer discovery & --net=host
ACP peer discovery uses a UDP broadcast on port 18802, which does not cross the
default Docker bridge. If you want the daemon to discover other Aura agents on the LAN, run
with --net=host. This is the only reason to use host networking — it is
not for GPU passthrough or inference.
docker run -d \
--name aura-daemon \
--net=host \
-v aura-data:/data \
-e AURA_WEB_TOKEN=$(uuidgen) \
coolkoo/aura-workshop:daemon-latest
--net=host gives the
containerized agent the host's network namespace. Treat the daemon image as the trust
boundary and only mount what the agent needs — never bind-mount your home directory into it.
Reference compose files ship at src-tauri/docker/docker-compose.daemon.yml and
docker-compose.remote.yml.
Reverse proxy + TLS
The daemon has no built-in TLS — it serves plain HTTP on 18800. Terminate
HTTPS at Caddy (a reference Caddyfile ships at
src-tauri/docker/Caddyfile) or any reverse proxy:
aura.example.com {
reverse_proxy localhost:18800
}
The daemon image has no in-app auto-update — pull a fresh image and re-run with the same env and volume to upgrade.
Headless mode
For servers without a display, the daemon image is the recommended path — it is headless by design. The desktop binary can also run without a window on a machine that has no display server:
xvfb-run aura-workshop
It starts without rendering a GUI and serves the full web UI for browser access. Every
feature remains available through the REST API and web UI. For production, prefer the Docker
daemon over an xvfb-wrapped desktop build.
Remote deployment
The desktop deploy_remote tool ships the daemon image to a remote host over
SSH and starts it there. This is isolation layer H — the same full-process
isolation as the daemon image, running on a remote machine.
- Provide the target: hostname, username, and an authentication method.
- Aura connects over SSH, detects the remote OS, and installs Docker if it is missing —
get.docker.comon Linux, Colima via Homebrew on macOS, or manual instructions on Windows. - It copies
docker-compose.remote.yml+ an.env, runsdocker compose up -dwith--net=host, and surfaces a one-time pairing code. - The remote daemon POSTs a heartbeat every 30s; the desktop app pairs and stores an auth token locally.
SSH authentication methods
| Method | Description |
|---|---|
| Key file | Path to an SSH private key (RSA, Ed25519, …). Most secure and recommended. |
| Password | Username/password via sshpass for non-interactive auth. |
| Saved credential | References an entry in the encrypted credential store; decrypted at deploy time. |
ssh + docker run to redeploy; use the deploy
flow so cleanup (deleting the container) happens correctly. Schedules, listeners, and
webhooks can each be targeted at a remote agent, and deleting the automation item also
tears down its remote deployment.
The eight isolation layers
There are eight distinct isolation layers with very different blast radius. Knowing which
one you are in matters. Layers A–F are desktop toggles; B/C/D are mutually exclusive
bash backends, while E and F are orthogonal and can stack on top.
Only G and H are strong process-level boundaries. Everything above them
isolates a subset of tools and leaves the rest running with host privileges.
| Layer | Trigger | What IS isolated | What is NOT |
|---|---|---|---|
| A. Native | native_mode=true (also the settings-load-failure fallback) | nothing | everything |
| B. Desktop Docker bash | native_mode=false + Docker available + not sandboxed | the bash command, inside debian:bookworm-slim | file ops, MCP, the SQLite DB, web tools, email |
| C. SSH backend | execution_backend=ssh | bash, executed on a remote host | local file ops, MCP, DB |
| D. Singularity backend | execution_backend=singularity | bash, inside a .sif container | local file ops, MCP, DB |
| E. Host sandbox | execution_isolation=sandbox | file reads/writes, via path rewrite into /tmp/aura-sandbox-{task_id} | bash, network, kernel |
| F. MCP per-task | mcp_servers.isolation="per_task" (stdio only) | stdio MCP child processes, with a per-task data dir | HTTP MCP servers (stay shared), bash, other tools |
| G. Daemon container image | running aura-daemon in the daemon image | everything — file, bash, MCP, DB all execute inside the container | nothing from the agent's view; the container IS the process |
| H. Remote deploy | deploy_remote ships Layer G to a remote host | same as G, on the remote host | nothing |
The bash routing switch
In the desktop app, the only thing deciding whether bash is containerized is a
single check: if !native_mode && docker_available && !sandbox_active.
When it does route to Docker, the container is minimal —
docker run --rm -v {project}:/workspace -w /workspace, with no
--network, no --user, and no blanket env forwarding.
native_mode load-failure fallback is insecure-open: if the
settings row fails to load (corrupt DB, transient lock, migration hiccup), Aura defaults to
host bash even if you had toggled Docker on — there is no fail-closed here. And if Docker
mode is on but the Docker binary can't be resolved at runtime, bash silently falls back to
native host execution.
(2) Layer B is bash-only. In desktop Docker mode, read_file,
write_file, edit_file, glob, grep,
web_fetch, email_send, MCP, and the SQLite DB all run on the host
with your privileges. The only thing Layer B stops is a destructive command inside a
bash invocation — a misbehaving agent can still read ~/.ssh/id_rsa, write
any file you can write, reach any HTTP endpoint, or read credentials straight out of the DB.
For untrusted workloads, run the daemon container (G) or a remote deploy (H) — never desktop
native_mode. The sandbox (E) also has a known limit: it blocks ..
traversal but does not normalize symlinks that already live inside the project tree,
so a read that follows a symlink pointing outside the project resolves on the host.
Guardrails registry
Aura ships a composable guardrails registry that runs at three stages — Input
(before the agent sees user content), Tool (before a tool call executes),
and Output (before a reply or tool result is returned). Each rule returns
Allow, Block, Redact, or a secret-capturing
Vault action. The first Block short-circuits the rest; multiple
Redacts chain; every firing rule is logged to a
guardrail_violations audit row.
| Rule | Stage | Default action | What it does |
|---|---|---|---|
bash-destructive-commands | Tool | Block | Substring match of the destructive bash blocklist. |
secret-detection | Input | Vault → Block | Detects API keys/tokens/PEM/JWT in user input, rewrites to {{credential:slug}} placeholders. |
secret-detection-output | Output | Redact | Scans tool results for secrets → [REDACTED-<kind>] (closes cat .env exfil). |
pii-detection | Input | Redact | SSN and Luhn-valid card numbers redacted; emails/phones deliberately pass through. |
pii-detection-output | Output | Redact | Same PII patterns on model output text/thinking. |
prompt-injection-signatures | Input | Block | Matches known override phrasings ("ignore previous instructions", …). |
output-max-tokens | Output | Redact | Truncates output over a ~32,000-token estimate, appending [TRUNCATED]. |
Always-on scan primitives
Independent of the toggleable registry, a few checks run unconditionally. These are recoverability fail-safes and defense-in-depth, not sandboxes — treat them as raising the bar, not sealing a path.
| Primitive | Where it fires | Behavior | Limitation |
|---|---|---|---|
| Hardcoded bash blocklist | every bash command | Blocks rm -rf /, rm -rf /*, mkfs, dd if=/dev/zero, fork bombs, > /dev/sda, chmod -R 777 / | Does not catch rm -rf $HOME, curl … | sh, or a non-bash tool doing equivalent damage |
redact_secrets | tool-result fallback when no guardrail context is available | Masks 8 secret patterns (OpenAI/Anthropic/AWS/GitHub/xAI/Bearer/PEM/JWT) with [REDACTED] | Regex-based; unknown token formats slip through |
is_ssrf_target | web_fetch | Blocks localhost, 0.0.0.0, ::1, cloud-metadata hosts, and any hostname resolving to loopback / RFC-1918 / link-local | DNS resolved once; a TOCTOU rebind is not defended. bash (e.g. curl) bypasses it entirely |
scan_for_injection | context files read via file_read | Warns (does not block) on 6 override phrasings | Advisory only; the blocking version is the opt-in injection guardrail |
Anything written into durable memory or the wiki also passes through a write-time threat scan first, which rejects content carrying injection phrasings, credential-exfil patterns, or invisible-unicode steganography — so a poisoned page cannot be planted for later recall. It is signature-based and evadable by novel phrasing.
Web-server authentication & TLS
- Bearer token — from
AURA_WEB_TOKEN(preferred) or theweb_server_tokensetting, compared in constant time. The token is the identity; there is no per-user scoping — anyone holding it is "the user". - Password auth (optional) —
POST /auth/loginissues a JWT session cookie; the hash is read from the DB per request so changes take effect immediately. - Strict remote mode — when
AURA_REMOTE_DEPLOYMENT=trueorAURA_VIEWER_MODE=true, the daemon rejects unauthenticated/api/*even if no token or password is configured. This is the correct production default; without it, a network-reachable daemon with no credentials would serve everything to anyone who finds the port. - Chrome-extension bypass — requests whose
Originstarts withchrome-extension://skip auth (the browser-automation channel).
Public routes (auth bypass): /auth/*, /api/health,
/api/heartbeat/*, /ws/*, /api/image-proxy,
/charts/*, /acp/ping, /.well-known/*, and
GET /acp/agents — the ACP discovery surface is intentionally
unauthenticated so peers can introspect. ACP mutation and run-bound endpoints
(/acp/runs, /acp/session/*) still require a token.
There is no built-in TLS. Plain HTTP behind a trusted token is acceptable on a known-trusted LAN; for any internet exposure, TLS at a reverse proxy is mandatory.
Encryption at rest
Sensitive values are encrypted with AES-256-GCM before they reach SQLite,
stored as enc:v1:{nonce}:{ciphertext} with a random 96-bit nonce per value.
Encrypted columns include the credential store, the credential pool, cloud connectors, and
OAuth access/refresh tokens; listener auth_config encrypts fields whose key
name looks sensitive.
security CLI. On platforms without that command — Linux,
Windows, and the daemon container — the keychain read/write fails, so Aura generates a fresh
ephemeral in-memory key each start: values encrypted in one run are not
decryptable in the next. On those hosts, treat the DB as protected only by the host
filesystem and rely on OS-level disk encryption (FileVault / BitLocker / LUKS) plus strict
permissions (chmod 600 the DB, lock down /data). Provider API keys
in settings and custom_providers are stored plaintext
by design — if the DB file leaks, those keys leak.
OAuth client_id values and the Composio API key are baked into the binary at
build time. These are designed to be visible — they identify the Aura app to OAuth
providers and function as public client identifiers, not per-user secrets.
Spend tracking & caps
The Billing tab tracks every LLM call so cost never surprises you. Four summary cards show today's cost, this month's cost, all-time total, and a per-provider monthly breakdown; an interactive 14-day bar chart plus per-model area charts show where spend is going; and a detailed table lists every model used with input/output tokens, request count, and total cost.
Spend limits & fallback
Set a maximum daily and monthly spend per provider. The limit is checked before every LLM call: when the current provider exceeds its cap — or hits rate limits (HTTP 429) or server errors — Aura walks the provider fallback order (drag-to-reorder) until it finds one that is in budget and reachable. This fallback applies only to Aura Routing and cost enforcement; a pinned model never silently switches. Pricing is pre-seeded with current market rates per model and editable, with a reset-to-defaults button.
During a task, the Context Panel shows real-time usage: context-window percentage, input / output / cache-read tokens, running cost, latency, and the active model and provider. Spend caps can also be published centrally by the Enterprise Portal, where they are enforced locally on every deployment.
Chrome extension
A companion Chrome extension provides a side-panel chat interface and lets agents drive the browser.
- Side panel — a full chat surface with Markdown/code rendering, streaming
responses, tool-usage display, and file attachments, connected over WebSocket at
/ws/sidepanel. It is tab-aware: the agent can see the active tab's URL and page context. - Context menus — right-click selected text, a link, or a page element to send it to the agent with its page context.
- Screenshot capture — grab the current tab and send it for visual analysis.
- Browser automation — through
/ws/browser, agents drive the browser with thebrowser_actiontool: navigate, click, type, screenshot, extract page content or DOM elements, scroll, and wait.
Setup
- Load the extension from the
extension/directory in Chrome's developer mode (chrome://extensions→ Developer mode → "Load unpacked"). - Point it at your Aura web server URL (e.g.
http://localhost:18800). - If the server has a token configured, enter it in the extension settings.
- The extension icon turns green when connected.
Note that extension requests bypass web-server auth by Origin
(above), so treat a machine with the extension installed as trusted.
Embeddable chat widget
Embed an Aura-powered chat interface on any website with a small script snippet. Configure it by creating a chatbot-type listener in the Listeners tab, then drop in:
<script src="http://your-server:18800/widget.js"></script>
<script>
AuraWidget.init({
serverUrl: "http://your-server:18800",
token: "your-auth-token", // optional
position: "bottom-right", // bottom-right or bottom-left
title: "AI Assistant",
greeting: "How can I help?",
theme: "dark" // dark or light
});
</script>
Messages stream in real time over WebSocket; responses render Markdown including code blocks; conversation history persists per user across page loads; and color scheme, position, title, and greeting are all configurable. When you expose the widget to the public internet, front it with TLS and scope what the underlying listener can do.
Network ports
| Port | Proto | Service | Exposure guidance |
|---|---|---|---|
| 18800 | TCP | Web UI + REST + SSE + WS + /acp/* REST + Gateway /v1/* | Behind a reverse proxy + auth |
| 18790 | TCP | Inbound webhooks (separate from the main API server) | Behind a reverse proxy + per-webhook secret |
| 18802 | UDP | ACP peer discovery broadcast | LAN only (needs --net=host in Docker) |
AURA_REMOTE_DEPLOYMENT=true so anonymous
access is impossible; enable disk encryption and chmod 600 the DB; explicitly
enable the guardrails you need (they ship off); set
spend limits on every paid provider; restrict egress to just
the providers you use; and gate consequential actions behind
human-in-the-loop workflow steps. Report security
issues to [email protected].