5.6 KiB
Deploying meeting bots
@elizaos/plugin-meetings joins Google Meet / Microsoft Teams / Zoom by driving
a real Chromium via playwright-core. That has hard host requirements: a
Chromium binary must be resolvable, and — because Meet's isTrusted-click
bot-detection is defeated with humanized XTEST input — a real X server should be
available even on a "headless" box. This doc is the deployment matrix.
How the plugin decides support & headless mode
Two typed resolvers own all of this (src/platform-support.ts):
resolveMeetingRuntimeSupport(runtime)→{ supported, reason?, headless, chromiumPath? }. Unsupported when the host is a mobile embedding (ELIZA_PLATFORM=android|ios) or when no Chromium is resolvable (no bundled playwright download, noELIZA_MEETINGS_CHROMIUM_PATH, and no system Chrome/Edge channel).resolveHeadlessMode(env, platform)→boolean:- explicit
ELIZA_MEETINGS_HEADLESS(true/1/yes/onvsfalse/0/no/off) wins; - else auto-detect — headed when a display exists (macOS/Windows always;
Linux only when
DISPLAYorWAYLAND_DISPLAYis set), headless otherwise.
- explicit
Headless uses Chromium's modern "new" headless (headless: true →
--headless=new), which keeps getUserMedia / WebAudio working. The classic
headless mode disabled them and is never used.
Chromium resolution precedence
ELIZA_MEETINGS_CHROMIUM_PATH— explicit binary (must exist, else a hard error).- Playwright's bundled Chromium (when the browser download is installed).
- System channel fallback —
chrome(Meet/Zoom) ormsedge(Teams).
Headed-under-Xvfb vs pure headless — the recommendation
Recommended: headed Chromium under Xvfb (ELIZA_MEETINGS_HEADLESS=false +
DISPLAY=:99). Google Meet cross-checks that admission clicks are trusted user
gestures; the humanized input path (XTEST) needs a real X display to synthesize
those, which Xvfb provides without a physical monitor. Pure headless
(--headless=new, no X server) is best-effort for Meet (it often trips the
anti-abuse interstitial) but reliable for Teams and Zoom, which do not gate
on XTEST-grade input. Pick pure headless only for a Teams/Zoom-only deployment.
(a) Local desktop — headed, system Chrome
macOS / Windows / a Linux desktop with a session. Turn on the meetings feature
in your agent config (features.meetings) — no env flag needed. A display is
always present, so the plugin auto-selects headed, and it drives the Chrome
or Edge you already have installed — no separate Chromium download.
# optional: pin a specific browser binary (otherwise your system Chrome/Edge is used)
# export ELIZA_MEETINGS_CHROMIUM_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
(b) Linux server / Eliza Cloud container — Xvfb + headed Chromium + PulseAudio
A headless VPS or an Eliza Cloud container has no display. Run headed Chromium under Xvfb, and run PulseAudio so Zoom's web client has an audio sink to capture from.
apt packages
RUN apt-get update && apt-get install -y --no-install-recommends \
# Chromium runtime deps
libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 \
libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 \
libpango-1.0-0 libcairo2 fonts-liberation \
# virtual display + humanized-input + clipboard
xvfb xdotool xclip \
# audio sink for Zoom capture
pulseaudio \
&& rm -rf /var/lib/apt/lists/*
env
Enable the meetings feature in the agent config baked into (or mounted onto)
the image; the vars below are runtime tuning, not an enable switch.
ENV ELIZA_MEETINGS_HEADLESS=false \
DISPLAY=:99
# Point at a Chromium binary if you don't ship playwright's bundled download:
# ENV ELIZA_MEETINGS_CHROMIUM_PATH=/usr/bin/chromium
launch under Xvfb
Wrap the agent process so it inherits the virtual :99 display and a running
PulseAudio daemon:
pulseaudio --start --exit-idle-time=-1
xvfb-run --server-num=99 --server-args="-screen 0 1280x720x24" \
bun run start
xvfb-run exports DISPLAY=:99; hasDisplay() then reports a display, so the
auto-detect picks headed even inside the container. Setting
ELIZA_MEETINGS_HEADLESS=false makes the mode explicit and logged regardless.
(c) iOS / Android on-device — NOT supported
Browser automation cannot run in a mobile app sandbox — there is no spawnable
Chromium, no XTEST, no PulseAudio. The plugin refuses to auto-enable on
ELIZA_PLATFORM=android|ios even when an env key is set, and
resolveMeetingRuntimeSupport() returns supported: false with a mobile
reason.
Mobile users still get meeting transcripts via one of:
- Route to a cloud-hosted agent — run the bot in an Eliza Cloud container/sandbox (topology (b) above) and consume the transcript from the mobile client over the dashboard/API. This is the intended path.
- The Discord / voice path — Discord "meetings" are voice channels owned by the Discord connector, which captures audio natively without a browser bot.
Anti-bot caveat (read before trusting Meet in production)
Google Meet actively detects datacenter egress + automation. The launcher
already omits the detectable --ignore-certificate-errors /
--disable-web-security flags, pins a Client-Hints-consistent User-Agent, and
strips navigator.webdriver. Even so, Meet admission is only reliable with
humanized XTEST input under a real X display (Xvfb). Treat pure-headless Meet
joins as best-effort; Teams and Zoom are robust headless.