Alpha — Experimental template for a secure, hosted OpenClaw. No production-readiness guarantees. Review all configurations before deploying with sensitive data.
Your own always-on AI assistant, running safely in the cloud and reachable from Microsoft Teams on your phone — not on your laptop. It runs on Azure Container Apps, talks to Azure OpenAI in Foundry Models with a Managed Identity (no API keys), and can offload risky tool execution to ephemeral, isolated sandboxes. Deploy it in one command — or just ask your AI agent to do it for you.
→ Jump to: Ask your agent · Deploy with devclaw · What can I do? · Advanced & reference
This repo ships an AI agent skill, so any assistant that reads
.github/copilot-instructions.md or
AGENTS.md — GitHub Copilot Chat, Claude Code, Cursor, Codex, and
friends — can set up and run everything for you in plain English. No need to
memorize azd env vars or scroll the troubleshooting tables.
How to use it: clone the repo, open it in VS Code with
GitHub Copilot Chat (or your preferred agent),
and just ask. (Already cloned? Your agent picks the skill up automatically. To add
it to another workspace, run npx skills add microsoft/openclaw-dev.) Try:
- "Deploy OpenClaw to
eastus2." - "Run tool execution in ephemeral sandboxes."
- "Connect it to Microsoft Teams so I can use it from my phone."
- "Why is
devclaw upfailing?" - "Stop it to save money, then start it again tomorrow."
- "Restrict access to just my team."
- "Tear it all down cleanly."
The assistant follows the playbook in
skills/openclaw-dev/SKILL.md and the always-on
rules in .github/copilot-instructions.md — using
this repo's own scripts, env-var contract, region list, and error catalog instead of
guessing. It always confirms with you before any destructive action (devclaw down,
az ad app delete, RBAC removal). The skill follows the open
Agent Skills format, so it works across many agents.
Prefer to drive it yourself? devclaw is a thin wrapper over the Azure Developer CLI (azd).
- Azure CLI + Azure Developer CLI
- An Azure subscription (free) and tenant where you can create Entra ID app registrations
- Either local Docker Desktop running or use the default
remoteBuild: truein azure.yaml (no local Docker needed; ACR builds the image) - PowerShell 7+ (
pwsh) if you're on Windows — needed bydevclaw teamsto build the Teams sideload zip
git clone https://github.com/microsoft/openclaw-dev
cd openclaw-dev
# macOS/Linux/WSL
./devclaw up
# Windows (cmd or PowerShell)
.\devclaw.cmd upOn first run, azd prompts for a subscription, region, and environment name (a new resource group rg-<env-name> is created automatically). First deploy takes ~6 minutes (provision ~2 min + remote build & deploy ~3.5 min). Subsequent devclaw deploy runs take ~3-4 minutes for a remote build, or ~30s if only the entrypoint changed.
devclaw status # Container state, FQDN, RG
devclaw logs # Tail logs until you see `[gateway] starting HTTP server`devclaw test only prints a hint that points you at the in-container console — it does not exercise the model end-to-end. The fastest real smoke test is the WebChat UI below.
After deployment, open the URL from devclaw status in your browser. If Entra ID Easy Auth is configured, you'll be prompted to sign in with your Microsoft account — after that the chat UI loads automatically with no further credentials needed.
devclaw up Deploy OpenClaw to Azure (provision + build + deploy)
devclaw test Print a hint to run the in-container smoke test
devclaw status Show container status, FQDN, RG
devclaw logs Stream live container logs
devclaw start Scale to 1 replica (resume after stop)
devclaw stop Scale to 0 replicas ($0, state preserved)
devclaw restart Restart the active revision
devclaw deploy Rebuild and deploy after code changes
devclaw exec-mode <inproc|sandbox> Choose where tools run (apply with up)
devclaw teams Set up Microsoft Teams integration (build sideload zip)
devclaw clone (sandbox host) Boot another OpenClaw from the existing image
devclaw down Delete ALL Azure resources and Entra app regs (nuke & pave)
devclaw login Switch Azure account
Two commands you'll likely add later: devclaw exec-mode sandbox (offload tool execution to ephemeral sandboxes) and devclaw teams (add the Microsoft Teams channel).
Your own always-on AI assistant — accessible from Teams on your phone, the WebChat UI, or any OpenClaw channel.
- "Summarize my meeting notes" — paste transcripts via Teams, get action items
- "Draft a reply to this email" — send the thread, get a polished response
- "Explain this error log" — paste a stack trace, get a diagnosis
- "Review this PR" — paste a GitHub PR link, get code review feedback
- "Write my weekly status" — the agent tracks your sessions
Everything above gets you deployed. The rest — why cloud instead of your laptop, the full architecture, the security model, Teams internals, and troubleshooting — is reference detail. For the complete env-var contract and error catalog, see skills/openclaw-dev/SKILL.md.
OpenClaw runs arbitrary code and can be deceived by prompt injection. Don't run it on your work machine. This template gives you an isolated, ephemeral container instead.
| Your laptop ❌ | This template ✅ | |
|---|---|---|
| Isolation | Shares your credentials | Ephemeral container — nothing to compromise |
| Credentials | API keys on disk | Managed identity — no keys anywhere |
| Nuke & pave | Reinstall OS | devclaw down && devclaw up (~6 min) |
| Always on | Only when open | Always on. devclaw stop = $0 |
| Teams/mobile | Only when laptop is on | Always connected |
| Cost | Your hardware | ~$2-5/day running, $0 stopped |
openclaw-dev is one cloud "brain" plus many disposable sandboxes:
- The openclaw brain runs on Azure Container Apps and calls Azure OpenAI in Foundry Models (default
gpt-5.4-mini) over a Managed Identity —disableLocalAuth: true, so there are no keys to leak. - Entra ID Easy Auth forces a Microsoft sign-in (scoped to your tenant) before anyone reaches the brain.
- Turn on sandbox execution (
devclaw exec-mode sandbox) and every untrusted tool run — shell, code, browser — is handed to an ephemeral, isolated ACA Sandbox that's destroyed after the task, so the brain is never exposed to the code it runs. - Microsoft Teams is an optional channel so you can reach it from your phone.
The diagram at the top of this README shows the full topology (docs/architecture-sandbox.svg).
| Mode | Turn on with | What runs where |
|---|---|---|
| In-process (default) | — | tools run inside the brain container |
| Sandbox execution | devclaw exec-mode sandbox → devclaw up |
tools run in throwaway ACA Sandboxes; the brain stays on ACA. Teams-compatible. |
| Sandbox host (experimental) | azd env set USE_SANDBOX true → devclaw up |
the whole brain runs in a sandbox; no Teams. |
| Resource | Purpose |
|---|---|
| Azure Container Apps | Hosts the OpenClaw "brain" gateway — public HTTPS on :18789, ephemeral container (host layer is swappable) |
ACA Sandboxes (with EXECUTION_MODE=sandbox) |
Ephemeral, isolated nodes the brain offloads untrusted tool execution to, via the sandbox MCP server (src/sandbox_mcp/); each is destroyed after the task |
| Azure OpenAI in Foundry Models | LLM backend via the OpenAI-compatible /openai/v1/ API — keyless (disableLocalAuth: true). Default: gpt-5.4-mini. (Scope to add Claude and other Foundry Models in the near future) |
| Azure Bot Service | Bot Framework registration that fronts the Teams channel; routes inbound Teams activity to the container's /api/messages |
| Managed Identity | Container → model auth via short-lived Entra ID tokens |
| Entra ID Easy Auth | Microsoft login required before reaching the WebChat UI. /api/messages is excluded so Bot Framework can call in with its own JWT |
| Azure Files | Persists credentials, workspace, sessions across restarts |
| Container Registry | Stores the container image |
| Log Analytics | Container and gateway logs |
Inside the container there are three Node processes started by src/entrypoint.sh:
| Process | Port | Role |
|---|---|---|
| gateway-proxy (src/gateway-proxy.mjs) | 0.0.0.0:18789 (public) |
Terminates ACA ingress; routes POST /api/messages to the msteams plugin on :3978 and everything else to the OpenClaw gateway on :18788 |
| OpenClaw gateway | 127.0.0.1:18788 |
WebChat UI + WebSocket API; loads the msteams plugin which spawns the webhook on :3978 |
| auth-proxy (src/auth-proxy.mjs) | 127.0.0.1:18790 |
Injects a fresh Entra ID bearer token from DefaultAzureCredential on every forwarded request to AOAI |
graph LR
User["👤 User<br/>Browser / Mobile"]
Teams["💬 Microsoft Teams<br/>Bot Framework"]
EasyAuth["🔐 Entra ID Easy Auth<br/>Microsoft login gate<br/>(excludes /api/messages)"]
subgraph Host["Host (Azure Container Apps today)"]
Proxy["🔀 gateway-proxy<br/>:18789"]
GW["🦞 OpenClaw Gateway<br/>:18788 · token auth"]
MST["📥 @openclaw/msteams<br/>:3978 · /api/messages"]
Auth["🔑 auth-proxy<br/>:18790 · injects MI bearer"]
end
AOAI["OpenAI-compatible model<br/>Microsoft Foundry Models / Azure OpenAI<br/>disableLocalAuth: true"]
MI["Managed Identity<br/>Entra ID token"]
AF["Azure Files<br/>credentials / workspace / sessions"]
User -->|"HTTPS"| EasyAuth
EasyAuth -->|"Authenticated"| Proxy
Teams -->|"Bot Framework JWT"| Proxy
Proxy -->|"/api/messages"| MST
Proxy -->|"all other paths"| GW
MST -->|"channel events"| GW
GW -->|"OpenAI REST API"| Auth
Auth -->|"Bearer token"| AOAI
GW -.->|"Volume mount"| AF
MI -.->|"RBAC: Cognitive Services User"| AOAI
All dependencies are pinned at container build time (see src/Dockerfile).
| SDK | Version | Role | Notes |
|---|---|---|---|
openclaw |
@latest (≥ 2026.5.26) |
The gateway runtime itself. Installed globally via npm install -g openclaw@latest |
Refreshed on every devclaw deploy (no version pin = always latest at build time) |
@openclaw/msteams |
2026.5.26 |
External OpenClaw plugin that owns the Teams channel: validates Bot Framework JWTs, parses activities, sends replies | Installed via openclaw plugins install npm:@openclaw/msteams. Bundles its own copies of the Teams SDKs below |
@microsoft/teams.api |
2.0.11 (plugin-bundled) / 2.0.6 (Docker-side compat) |
Microsoft's current Teams SDK — REST client for the Bot Connector and Graph surfaces. Successor to the deprecated botbuilder line |
v2.0 line went GA in late 2024; roughly 12–18 months old (mid-2024 → May 2026) |
@microsoft/teams.apps |
2.0.11 (plugin-bundled) / 2.0.6 (Docker-side compat) |
High-level Teams app/agent framework — message routing, conversation state, adapters. Built on top of teams.api |
Same generation as teams.api; roughly 12–18 months old |
@azure/identity |
4.13.1 (plugin-bundled) / latest (auth-proxy) |
Used by the auth-proxy and the msteams plugin for DefaultAzureCredential and getBearerTokenProvider — fetches/caches/refreshes Entra ID tokens for AOAI and Bot Framework |
The 4.x line has been the active major since early 2024 |
http-proxy |
latest (auth-proxy install) | Powers src/gateway-proxy.mjs — splits ingress by URL path | Long-lived, stable library |
How AOAI/Foundry is accessed: OpenClaw speaks the OpenAI-compatible REST API — the Chat Completions API (api: "openai-completions" in src/openclaw.json) at /openai/v1/chat/completions — directly. It does not depend on the official openai npm SDK or the older @azure/openai SDK. Requests flow gateway → auth-proxy → AOAI/Foundry; the auth-proxy attaches the MI bearer token at the wire level, so AOAI's disableLocalAuth: true works without API keys anywhere in the system. The auth-proxy is path-agnostic (it forwards req.url as-is), so the same proxy works for any OpenAI-compatible surface — chat completions, responses, embeddings, audio, images — if you swap the model adapter in openclaw.json.
How Teams is accessed: Inbound activities come in over HTTPS from Bot Framework to /api/messages. The @openclaw/msteams plugin validates the JWT and uses @microsoft/teams.api + @microsoft/teams.apps for everything from there — activity dispatch, replies, streaming, adaptive cards.
This template applies four independent layers of security. An attacker must defeat all of them to reach the AI backend:
| Layer | What it does |
|---|---|
| 1. Entra ID Easy Auth | Microsoft login required before any request reaches the container. Deployed automatically by devclaw up. Unauthenticated requests get a 401. Scoped to your tenant. |
| 2. Gateway token | A random per-container token is injected into the SPA at startup. Even an authenticated user cannot call the WebSocket API without it. |
| 3. Managed Identity (no API keys) | The container authenticates to the model endpoint via short-lived Entra ID tokens. disableLocalAuth: true means API keys don't even exist. |
| 4. Ephemeral container | State is on Azure Files; the container itself is disposable. devclaw down && devclaw up = clean slate in 6 minutes. |
- Skills run arbitrary code — a malicious skill can access the managed identity. Only install trusted skills
- Prompt injection — OpenClaw is susceptible. Nuke and repave if behavior changes
- Container runs as root — add a non-root user for hardened deployments
- Conversations flow through the model endpoint — don't paste highly sensitive data
Easy Auth is configured automatically by devclaw up. The preprovision hook creates an Entra ID app registration, Bicep enables the auth config on the Container App, and the postprovision hook updates the redirect URI. No manual steps needed.
To restrict access to specific users or groups, update the app registration in the Azure Portal:
- Azure Portal → Entra ID → App registrations →
openclaw-auth-<env> - Properties → Assignment required? → Yes
- Enterprise applications → assign specific users/groups
- Don't paste confidential data — conversations flow through the configured model endpoint
- Don't install credential-heavy skills — no email, bank, or internal API skills
- Nuke and pave regularly —
devclaw down && devclaw upif anything seems off - Monitor logs —
devclaw logs
Connect OpenClaw to Microsoft Teams so you can chat with it from your phone.
devclaw teamsThis automatically:
- Enables the Microsoft Teams channel on your Azure Bot (created by
devclaw up) - Builds a sideloadable Teams app package (
teams/openclaw-teams-app.zip)
- Teams → Apps → Manage your apps → Upload a custom app
- Select
teams/openclaw-teams-app.zip - Add → DM the bot to test
The Teams channel is handled by @openclaw/msteams — an external OpenClaw plugin installed at container-build time. It opens its own Express server on :3978 for the Bot Framework webhook (/api/messages). Because ACA exposes only a single public port, src/gateway-proxy.mjs listens on :18789 and routes /api/messages to the plugin and everything else to the OpenClaw gateway.
The plugin must be explicitly activated in src/openclaw.json — channels.msteams.enabled: true alone is not enough for external (non-bundled) plugins. The repo ships with the required block already in place:
"plugins": {
"enabled": true,
"allow": ["msteams"],
"entries": { "msteams": { "enabled": true } }
}If you ever see startup logs like [gateway] http server listening (N plugins: …) without msteams in the list, that block is missing or has been overwritten by a stale state file on Azure Files — the entrypoint restores src/openclaw.json from the canonical copy on every boot to prevent this drift.
| Symptom | Cause | Fix |
|---|---|---|
Activating for >2 min |
Token acquisition retrying | Normal — allows up to 5 min. Check devclaw logs |
ActivationFailed |
Container crashed | Check Azure Portal → Container App → Log stream |
Cannot find module '@buape/carbon' |
Cached broken Docker layer | docker build --no-cache ./src then devclaw deploy |
Config invalid: Unrecognized key |
Old config format | Config must be {"gateway":{"mode":"local"}} |
azd provision fails with Circular dependency detected on resource ... containerApps |
Old aca.bicep with existing self-reference |
Pull latest — the template now uses a containerImage parameter sourced from SERVICE_OPENCLAW_IMAGE_NAME |
| Symptom | Cause | Fix |
|---|---|---|
401 invalid issuer |
RBAC not propagated | Wait 5 min. Verify: az role assignment list --assignee <principal-id> --all |
| Token retries failing | IMDS slow to initialize | Normal — retries for 60s |
disableLocalAuth blocks list-keys |
By design | Expected — managed identity only |
| Symptom | Cause | Fix |
|---|---|---|
| HTTP 500 on all routes | Missing plugin deps | Rebuild with docker build --no-cache ./src |
pairing required |
Missing dangerouslyDisableDeviceAuth or trustedProxies in config |
Ensure src/openclaw.json has both settings (see repo) |
Proxy headers detected from untrusted address |
Reverse proxy not trusted | Add gateway.trustedProxies with your proxy CIDRs |
| WebChat shows login screen | Token not injected | Check entrypoint.sh runs successfully — see devclaw logs |
| Symptom | Cause | Fix |
|---|---|---|
POST /api/messages returns 502 |
msteams plugin didn't load → nothing listening on :3978 |
Confirm plugins.entries.msteams.enabled: true and plugins.allow: ["msteams"] are present in src/openclaw.json, then devclaw deploy. Look for [gateway] http server listening (… msteams …) in devclaw logs |
POST /api/messages returns 401 with {"error":"Unauthorized"} |
Working as designed — Bot Framework JWT auth is rejecting the unsigned curl request | None. Real Teams traffic carries a valid bearer token and is accepted |
Diagnostic block in logs says @openclaw/msteams package: MISSING |
openclaw plugins install npm:@openclaw/msteams failed during the Docker build |
Rebuild with docker build --no-cache ./src and check the build output |
| Bot replies in WebChat but not in Teams | Teams channel not enabled on Azure Bot, or sideload uses wrong botId |
Re-run devclaw teams (re-enables the channel and rebuilds the zip with the current bot app id) |
[gateway] http server listening (7 plugins: browser, canvas, …) with no msteams |
Plugin activation rule not met (see "How the Teams integration works" above) | Verify the plugins block in src/openclaw.json matches the canonical copy shipped in the repo |
| Symptom | Cause | Fix |
|---|---|---|
az containerapp exec crashes |
Azure CLI Unicode bug (🦞) | Use Azure Portal Console instead |
az containerapp logs hangs |
SSL issue | Use Azure Portal Log stream |
azd up warns about permissions |
azd heuristic | Safe to proceed. Or add User Access Administrator role |
The deployed model is reachable via the OpenAI-compatible /openai/v1/ API:
TOKEN=$(az account get-access-token --resource "https://cognitiveservices.azure.com" --query accessToken -o tsv)
ENDPOINT=$(az cognitiveservices account list -g <rg> --query "[0].properties.endpoint" -o tsv)
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"model":"gpt-5.4-mini","messages":[{"role":"user","content":"Hello"}]}' \
"$ENDPOINT/openai/v1/chat/completions"devclaw down # Destroys ALL resources