A terminal that connects builders, Agora platform, and AI agents. Manage Agora projects and tokens, route tasks between Astation and AI coding agents, generate diagrams, drive voice-powered coding workflows, and more -- all from a single CLI/TUI.
npm install -g @agora-build/atemOr via shell script:
curl -fsSL https://dl.agora.build/atem/install.sh | bashOr download a binary from Releases.
git clone git@github.com:Agora-Build/Atem.git
cd Atem
cargo build --release
# Binary at target/release/atematem # Launch TUI
atem repl # Interactive REPL with AI command interpretationatem login # Log in with Agora Console (opens browser)
atem logout # Log out from SSOatem token rtc create # Generate RTC token (interactive)
atem token rtc create --channel test --rtc-user-id 0 --expire 3600
atem token rtc create --channel test --rtc-user-id alice --with-rtm # RTC + RTM in one token (reuses rtc-user-id)
atem token rtc create --channel test --rtc-user-id 42 --with-rtm --rtm-user-id bob # Separate RTM account
atem token rtc decode <token> # Decode existing RTC token
atem token rtm create # Generate Signaling (RTM) token
atem token rtm create --rtm-user-id bob --expire 3600
atem token rtm decode <token> # Decode existing Signaling (RTM) tokenatem project list # List Agora projects
atem project list --show-certificates # List with app certificates visible
atem project use <APP_ID> # Set current project by App ID
atem project use <N> # Set current project by index (1-based)
atem project show # Show current projectatem agent list # Scan and list detected AI agents
atem agent launch # Launch Claude Code as PTY agent
atem agent launch codex # Launch Codex as PTY agent
atem agent connect <WS_URL> # Connect to ACP agent and show info
atem agent prompt <WS_URL> "text" # Send prompt to ACP agent
atem agent probe <WS_URL> # Probe URL for ACP support
atem agent visualize "topic" # Generate visual HTML diagram via agent
atem agent visualize "topic" --url ws://localhost:8765 # Explicit agent URL
atem agent visualize "topic" --no-browser # Skip opening browseratem vault new --summary "auth refactor context" # Create a vault, prints its id
atem vault list # List vaults you can read
atem vault read --vault-id <id> # Current contents
atem vault read --vault-id <id> --history # Every version (shown eN vM)
atem vault read --vault-id <id> --format plain # Bare text (pipe into a prompt)
atem vault write --vault-id <id> --text "decided: JWT in cookie" # Append an entry
atem vault write --vault-id <id> --entry-id 3 --text "..." # Override entry e3
atem vault set-summary --vault-id <id> --text "..." # Update the summaryLets multiple atems (each driving an agent) share a versioned, append-only
context store via the relay. Requires a relay-hosted vault server (/api/vault);
see designs/vault.md.
atem serv convo # Launch ConvoAI test page (HTTPS)
atem serv convo --config ~/convo.toml # Use custom config file
atem serv convo --background # Detached daemon — POSTs /join, registers, exits
atem serv attach <ID> # Open UI bound to a running convo daemon (talk to live agent)
atem serv attach 3 # …or by index from `atem serv list`
atem serv rtc # Launch RTC test page (HTTPS)
atem serv rtc --channel test --port 8443
atem serv rtc --background # Run as background daemon
atem serv diagrams # Host diagrams from SQLite (HTTP)
atem serv diagrams --port 9000
atem serv diagrams --background
atem serv webhooks # Receive Agora webhooks locally (auto-tunneled via ngrok)
atem serv webhooks --no-tunnel # Local listener only — bring your own tunnel
atem serv webhooks --background # Run as background daemon
atem serv list # List running background servers (#, ID, PID, STATUS)
atem serv kill <ID|#> # Kill a server (POSTs /leave for convo)
atem serv killall # Kill all background servers
# Fleet test loop — {appid} and {ts} are expanded by atem
for i in $(seq -f '%04g' 1 10); do
atem serv convo --background --channel 'atem-convo-{appid}-{ts}-'$i
doneserv convo — ConvoAI voice agent: live transcription (RTM), preset selection, avatar (Akool, LiveAvatar, Anam), RTC Stats, API History, camera toggle, RTC encryption (key + salt forwarded to the agent), HIPAA mode (routes via /hipaa/api/...), audio dump.
--background re-execs as a detached daemon: parent POSTs /join, registers the agent in ~/.config/atem/servers/<channel>.json, and exits. The daemon polls Agora's GET /agents/{id} every 60s and writes the status (RUNNING/IDLE/STOPPED/…) back into the registry — atem serv list reads it without making any network calls. kill/killall SIGTERM the daemon, which catches the signal and POSTs /leave before exiting. The daemon's log file (<channel>.log) contains the /join URL and request body with secrets masked, useful for debugging encryption mismatches.
serv attach <id> — opens a foreground HTTPS UI bound to a running convo daemon's channel. The page hides Start/Stop because the daemon owns the agent; you Join to talk to the live agent. Encryption / HIPAA / geofence are read from the same convo.toml so the local SDK matches what the daemon used.
serv rtc — RTC test page: join/leave, publish/subscribe audio+video, token generation, RTM messaging, RTC encryption (8 modes; gcm2 modes auto-generate a copyable salt).
serv diagrams — SQLite-backed HTTP server for hosting AI-generated HTML diagrams.
serv webhooks — local receiver for Agora webhook events (ConvoAI: agent_joined, agent_left, agent_history, agent_error, agent_metrics, …; RTC NCS events). Validates Agora-Signature-V2 (HMAC-SHA256) when a secret is configured in webhooks.toml; accepts unsigned events with a banner warning otherwise. Live web console at http://127.0.0.1:9090/ shows incoming events; each event also prints a one-line summary to stdout. Default tunnel provider is ngrok (requires ngrok config add-authtoken once); set tunnel_provider = "cloudflared" for zero-auth quick tunnels. Use --no-tunnel if you're running cloudflared / ngrok separately for stable URLs across atem restarts. See configs/webhooks.example.toml for the full config schema.
Channel placeholders — --channel accepts {appid} (first 12 chars of the active app id) and {ts} (unix epoch seconds at startup). Useful in for-loops: --channel 'atem-convo-{appid}-{ts}-001' produces atem-convo-2655d20a82fc-1777574763-001.
atem config show # Show resolved config
atem config set astation_ws <URL> # Set Astation WebSocket URL
atem config set astation_relay_url <URL> # Set Astation relay URL
atem config clear # Clear active project selection
atem config convo # Interactive wizard to configure ConvoAI agent
atem config convo --validate # Validate ConvoAI config without modifying
atem config convo --config <PATH> # Use a specific config fileThe atem config convo wizard walks you through Channel & User → Agent → Preset (empty = skip) → Add Custom override? → Pipeline → Avatar. Preset and Custom stack: preset sets defaults, explicit blocks override per-field. Pipeline selects one of:
- Cascaded (ASR + LLM + TTS): 10 ASR, 9 LLM, 12 TTS vendors
- Multimodal LLM: OpenAI Realtime (
gpt-realtime), Google Gemini Live, xAI Grok Realtime — single model handles voice in/out - Avatar: Akool, LiveAvatar, Anam
convo.toml is structured into two atem-managed top-level sections plus the Agora pass-through tables. The web UI pre-fills form fields from [atem]; --background reads them directly.
[atem] # atem's runtime control surface
channel = "..." # auto-generated when omitted
rtc_user_id = "0" # human's RTC uid
pipeline = "cascaded" # "cascaded" | "mllm" — picks which block goes to /join
hipaa = false # route via /hipaa/api/... (Agora support must enable)
geofence = "GLOBAL" # GLOBAL | NORTH_AMERICA | EUROPE | ASIA | JAPAN | INDIA
enable_avatar = false # opt in to use [agent.avatar] this session
[atem.encryption] # mode = 0 → off
mode = 8 # 1..=8 (Agora's table); 8 = AES_256_GCM2 (recommended)
key = "your-key-here"
salt = "Q4mTLy5h…=" # base64 32 bytes; required for gcm2 modes (7, 8)
[agent] # the AI agent
user_id = "1001"
idle_timeout_secs = 120
preset = "preset_a, preset_b" # comma-separated; UI splits into checkboxes
[agent.llm] # cascaded providers (forwarded under properties.<svc>)
[agent.asr]
[agent.tts]
[agent.mllm] # OR a single multimodal model — replaces asr+llm+tts
[agent.avatar]
[advanced_features] # pass-through (forwarded verbatim to Agora)
[vad]
[sal]
[parameters]Both peers must use matching encryption/geofence or audio fails silently. enable_avatar is opt-in even when [agent.avatar] is configured — fleet --background launches won't burn avatar minutes by accident. atem config convo --validate reports any issue (unknown geofence, gcm2 mode without salt, missing [agent].user_id, etc.).
Atem tries a configured direct endpoint first and then the identity relay after
it has learned the target Astation identity. Direct and relay connections use
the same device session, so one Atem can move between networks without pairing
again. Successful authentication stores astation_relay_code automatically;
manual configuration is only needed for an override or headless provisioning.
| Atem location | Endpoint | First connection | Internet required |
|---|---|---|---|
| Same Mac | ws://127.0.0.1:8080/ws |
Transparent same-user proof | No |
| Another LAN machine | ws://<astation-ip>:8080/ws |
Approve pairing in Astation | No |
| Remote network | Public WSS identity relay | Approve pairing in Astation | Yes |
All paths use device authentication v2. Astation sends a fresh challenge; Atem proves possession of the local bootstrap token or its saved device-session token with HMAC-SHA256. A session ID by itself is not accepted. Same-Mac operation continues when Wi-Fi is disabled, and LAN operation needs no relay once the IP is configured.
Direct LAN currently uses plaintext ws://. Authentication prevents a stolen
session ID from being sufficient, but it does not stop traffic inspection or an
active LAN attacker during initial pairing. Treat direct LAN as pre-production
until WSS certificate pinning is implemented. The wire contract and test matrix
are documented in designs/session-auth.md.
| Mode | Description |
|---|---|
| Token Gen | Generate Agora RTC/RTM tokens locally |
| Projects | Browse and select Agora projects |
| Claude Chat | Claude Code integration via PTY |
| Codex Chat | Codex terminal integration via PTY |
atem agent visualize "topic" sends a prompt to a running AI agent (via ACP), which generates a self-contained HTML diagram and saves it to ~/.agent/diagrams/. Atem detects the new file, hosts it via atem serv diagrams, and opens it in the browser. Use --url ws://host:port to target a specific agent.
Astation integration and voice-driven coding are in progress — see ROADMAP.md.
atem login # Opens browser to log in with Agora ConsoleIf the browser redirect doesn't complete within 5 seconds (e.g. remote server), atem will prompt you to paste the callback URL from the browser address bar.
Files in ~/.config/atem/:
| File | Contents | Encryption |
|---|---|---|
config.toml |
Non-sensitive settings (extra_hostnames, SSO/BFF overrides) | None |
convo.toml |
ConvoAI agent config (API keys, provider params) | None (chmod 0600) |
credentials.enc |
SSO tokens | AES-256-GCM (machine-bound) |
project_cache.enc |
Project list + active project selection | AES-256-GCM (machine-bound) |
sessions.json |
Per-Astation device session IDs and tokens | Plaintext (chmod 0600) |
Encrypted files are bound to the machine they were created on — copying them to another machine won't decrypt.
~/.config/atem/config.toml:
# astation_ws = "ws://127.0.0.1:8080/ws"
# astation_relay_url = "https://station.agora.build"
# astation_relay_code = "astation-..." # learned automatically after authentication
# bff_url = "https://agora-cli.agora.io"
# sso_url = "https://sso2.agora.io"
# Extra hostnames baked into the self-signed cert + shown as "Custom:" URLs
# extra_hostnames = ["genie.netbird.cloud", "dev.mytailnet.ts.net"]ATEM_BFF_URL=... # Override BFF API base URL
ATEM_SSO_URL=... # Override SSO base URL
ASTATION_WS=... # Direct loopback, LAN, or VPN WebSocket endpoint
ASTATION_RELAY_URL=... # Relay HTTP(S) base URL
ASTATION_RELAY_CODE=... # Target Astation identity room
AGORA_APP_ID=... # Override active project App ID
AGORA_APP_CERTIFICATE=... # Override active project certificatecargo build # Debug build
cargo build --release # Release build
cargo test # Run tests (500+ tests)
cargo check # Type-check
cargo fmt # Format
cargo clippy # Lint
./scripts/run-local-dev-tests.sh # End-to-end CLI smoke test
./scripts/release.sh # Patch-bump Cargo.toml + commit + tag
./scripts/release.sh 0.5.0 # Explicit versionReleases are tag-driven — pushing vX.Y.Z triggers
.github/workflows/release.yml, which builds
the binaries and publishes @agora-build/atem to npm.
Use ./scripts/release.sh rather than git tag directly — the script keeps
Cargo.toml in sync with the tag, so atem --version reports the right number.
| Flag | Description |
|---|---|
real_rtm |
Link against Agora RTM SDK (default: stub) |
openssl-vendored |
Build OpenSSL from source (for cross-compilation) |
See ROADMAP.md for upcoming features and where to help. Items labelled help wanted are open for contributors.
- Astation -- macOS menubar hub that coordinates Chisel, Atem, and AI agents — talk to your coding agent from anywhere
- Chisel -- Dev panel for visual annotation and UI editing by anyone, including AI agents
- Vox -- AI latency evaluation platform
MIT