Install OpenClaw with its one-line installer or npm, run onboarding against a local Ollama model so there is no API bill, keep the gateway on loopback, connect Telegram with pairing on so strangers get a code and nothing else, add one scheduled job, and run openclaw security audit. Then read the ClawHub and ClawJacked findings before installing any skill.
Part of the Always-on agents series, post 16 of 33.
I have a rule for any software that holds credentials and runs unattended: it gets its own machine, or at least its own user, and it starts with the smallest set of permissions that still does the job. OpenClaw is a good test of that rule, because its defaults are mostly sensible and its ecosystem has had real problems. I compared it with the alternatives in the open-source comparison; this post is the setup, in the order I would do it.
Before you install: the machine, the user, the model
Three decisions first, and I make them before I type the install command. The machine: something that stays on and that you do not also use for banking. A Mac mini is the community favourite; a small Linux box or a free cloud VM works. The guides for a Mac and Linux in this series cover keeping it awake and keeping it alive. The user: OpenClaw's own installer sets up a user-level service, so create a dedicated account for it rather than running it as you. The model: start local and free. OpenClaw talks to Ollama natively, and a local model means the agent can explore your files without a single token leaving the machine. Switch to a hosted model later, for the decisions that need it, once you know what the agent can see.
Step 1: install
The documented route is the installer script, which detects the operating system, installs Node if it is missing and starts onboarding. OpenClaw wants Node 24.16 or newer and recommends 26. I used the npm route in my container because the Node was already there:
Terminal# the documented one-liner (macOS, Linux, WSL2)
curl -fsSL https://openclaw.ai/install.sh | bash
# or, with Node 24.16+ already installed (npm 11.16+ needs the allow-scripts flag).
# As a non-root agent user, give npm a prefix it can write to first:
export NPM_CONFIG_PREFIX="$HOME/.npm-global"; mkdir -p "$NPM_CONFIG_PREFIX"
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
npm install -g openclaw@latest --allow-scripts=openclaw
openclaw --versionOutput$ node --version; npm --version
v26.12.0
11.20.0
$ npm prefix -g
/home/node/.npm-global
$ openclaw --version
OpenClaw 2026.10.5 (1b58cb9)The prefix lines are there because my first two attempts failed. Running npm install -g as the unprivileged node user, which is exactly the dedicated-account setup I recommend, hit a permissions error on the image's root-owned global directory, and npm's advice was to run it as root. The better answer is a prefix the agent's user owns. The official installer script handles this for you by keeping OpenClaw and Node under its own directory; if you take the npm route on a Linux box, you have to do it yourself, and the error message will not tell you that.
Two notes from the install page that matter later. On macOS the background service is a LaunchAgent; on Linux and WSL2 it is a systemd user service; on native Windows it is a Scheduled Task. And openclaw onboard --install-daemon both configures the agent and installs that service, so if you want to look before it runs at boot, onboard first and install the service separately with openclaw gateway install.
Step 2: a free local model through Ollama
Onboarding has an Ollama choice. The non-interactive form is what I used, because my Ollama was in another container on a private network rather than on localhost:
Terminal# on the Ollama side: pull a model that can call tools
ollama pull qwen2.5:0.5b # tiny, for the test; the docs suggest gemma4
# on the OpenClaw side
export OLLAMA_API_KEY=ollama-local # any value; local hosts need no real key
openclaw onboard --non-interactive --accept-risk --skip-health \
--auth-choice ollama \
--custom-base-url "http://ao-ollama:11434" \
--custom-model-id "qwen2.5:0.5b"
openclaw models list --provider ollamaOutputDefault Ollama model: qwen2.5:0.5b
Workspace OK: ~/.openclaw/workspace
Sessions OK: ~/.openclaw/agents/main/sessions
Updated config: ~/.openclaw/openclaw.json
Backup: ~/.openclaw/openclaw.json.bak
$ openclaw models list --provider ollama
Model Input Ctx Local Auth Tags
ollama/qwen2.5:0.5b text 33k no yes defaultTwo things worth noticing. Onboarding backs up the config before it writes it, which I appreciated more than I expected to. And the model shows as Local: no, because from OpenClaw's point of view my Ollama was on another host on the private network; on a single machine with Ollama on localhost it would say yes.
The one trap the docs call out, and which I would have fallen into: point OpenClaw at Ollama's native URL, not the OpenAI-compatible /v1 endpoint. The compatibility endpoint breaks tool calling and the model starts printing raw tool-call JSON as text. Also, automatic model discovery only considers models with tool support and at least 16K of context that are already loaded in memory, so name the model explicitly as I did above rather than expecting it to be found.
Step 3: the gateway, and where it listens
The gateway is the long-lived process: it holds the chat connections, runs the scheduler and serves a dashboard. The security page says that on a regular host install it binds to loopback, so nothing outside the machine can reach it, and that container images are the exception: they default to an exposed bind and must be paired with auth. I checked what mine did:
Terminalopenclaw gateway # foreground, for a first look
openclaw gateway status
# dashboard: http://127.0.0.1:18789/Output$ openclaw gateway status
Service: no supported service manager detected (diagnostic only, not the check target)
File logs: /tmp/openclaw/openclaw-2026-10-21.log
Gateway: bind=loopback (127.0.0.1), port=18789 (env/config)
Check target: ws://127.0.0.1:18789
Dashboard: http://127.0.0.1:18789/
Check note: Loopback-only gateway; only local clients can connect.
CLI version: 2026.10.5 (~/.npm-global/bin/openclaw)
Gateway version: 2026.10.5
$ grep listening /tmp/openclaw/openclaw-2026-10-21.log
[gateway] http server listening (14 plugins: anthropic, browser, canvas, cua-computer, device-pair, file-transfer, geolocation, github, linux-node, memory-core, ollama, openai, talk-voice, xai; 3.9s)Loopback, as documented, and it took about four seconds to come up with fourteen plugins loaded, several of which I would rather were not there on a box that only needs Ollama and Telegram. The no supported service manager line is because my container has no systemd; on a real Linux install openclaw gateway install creates a user service, which is the always-on part, and the Linux guide in this series covers keeping it alive. openclaw doctor also suggested two environment variables for low-power hosts, a Node compile cache and OPENCLAW_NO_RESPAWN=1, which I would set on a Raspberry Pi.
If you ever need the dashboard from another device, the docs point at Tailscale or an SSH tunnel rather than opening the port, and they have an exposure runbook with a pre-flight and rollback checklist. I would not expose it at all. Everything I want from the agent arrives in a chat app.
Step 4: Telegram, with pairing on
This is the step I could not finish in a container, because a bot token is tied to a real Telegram account and I was not going to put mine in a test box. Here is exactly what the setup page says to do, and the part I care about is the default.
- Create the bot. Message @BotFather in Telegram, send /newbot, follow the prompts, copy the token. Check the handle is exactly @BotFather; impostors exist.
- Give OpenClaw the token. Either openclaw channels add --channel telegram --token <bot-token>, or the config block below, or the TELEGRAM_BOT_TOKEN environment variable. A tokenFile beats botToken, which beats the environment variable.
- Start the gateway and probe. openclaw gateway start, then openclaw channels status --probe.
- Approve yourself. Send the bot any message. It answers with a pairing code. Run openclaw pairing list telegram, then openclaw pairing approve telegram <CODE>. Codes expire after an hour.
~/.openclaw/openclaw.json{
channels: {
telegram: {
enabled: true,
botToken: "123:abc", // from @BotFather; or tokenFile, or TELEGRAM_BOT_TOKEN
dmPolicy: "pairing", // unknown senders get a pairing code, nothing else
groups: { "*": { requireMention: true } },
},
},
}The default dmPolicy is pairing, which means an unknown sender gets a code and nothing else, and groups default to an allowlist behind a mention. Leave both alone. The permissions post in this series is largely about what happens when an agent answers whoever writes to it, and OpenClaw's defaults here are the right ones.
Step 5: one scheduled job
OpenClaw's scheduler is called Automations, with openclaw cron kept as an alias. It takes a cron expression or an interval, a timezone, and either a system event or a model-backed message. I created the smallest useful job, a morning note, and listed it:
Terminalopenclaw automations create "0 7 * * *" \
--name "Morning note" \
--message "Write one line: what is due today." \
--session isolated \
--tz Europe/London
openclaw automations listOutput{
"id": "8044e91c-675d-4b79-b93a-5d2d2e1229ae",
"name": "Morning note",
"enabled": true,
"schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "Europe/London" },
"sessionTarget": "isolated",
...
}
$ openclaw automations list
ID Declaration Name Schedule Next Status Target Delivery
5aad561b heartbeat:main Heartbeat (main) every 30m in 28m idle main not requested
de64bfec memory-core:memory.. Memory Dreaming ... cron 0 3 * * * (exact) in 12h idle isolated not requested
8044e91c - Morning note cron 0 7 * * * @ Europe/London.. in 15h idle isolated announce -> last (last -> no route, will fail-closed)The list is more interesting than my job. OpenClaw had already scheduled two automations of its own before I added one: a heartbeat on the main session every thirty minutes, and a nightly memory consolidation at three in the morning. And my job's delivery column says it will fail closed, because I had not connected a channel yet: with nowhere to announce the result, it refuses rather than dropping the output somewhere I would not see. That is the right default, and it is the reason the Telegram step belongs before the first scheduled job on a real install.
Two details from the schedules page worth knowing. Top-of-the-hour cron expressions are staggered by up to five minutes unless you add --exact, which is a kind choice for a shared machine and a surprise if you expected 07:00. And --session isolated gives each run a fresh session, which is what I want for a job: no memory of yesterday's run leaking into today's note. To deliver a job's result to a chat, add --announce --channel telegram --to <chat id>.
Step 6: run the audit, then read what researchers found
OpenClaw ships a one-command check of your own setup, and I ran it before anything else got installed:
Terminalopenclaw security auditOutputOpenClaw security audit
Summary: 1 critical · 1 warn · 1 info
CRITICAL
models.small_params Small models require sandboxing and web tools disabled
Small models (<=300B params) detected:
- ollama/qwen2.5:0.5b (0.5B) @ agents.defaults.model.primary (unsafe; sandbox=off; web=[web_search, web_fetch, browser])
Uncontrolled input tools allowed: web_search, web_fetch, browser.
Fix: If you must use small models, disable web_search/web_fetch/browser globally or for each small model
with tools.byProvider["provider/model"].deny=["group:web","browser"]; use agents.defaults.sandbox.mode="all"
WARN
gateway.trusted_proxies_missing Reverse proxy headers are not trusted
INFO
summary.attack_surface groups: open=0, allowlist=0; tools.elevated: enabled; browser control: enabled
trust model: personal assistant (one trusted operator boundary), not hostile multi-tenantThe audit flagged me, and it was right. I had put a tiny model in charge and left it the web tools, which is the combination a prompt-injection attack wants: a model too small to notice it is being steered, reading pages an attacker can write. The fix it prints is the one I would apply before letting this agent read anything from the internet: deny the web group and the browser for the small model, or turn the sandbox on for everything. I did not apply it in the test container, so the output above is the honest before state. On the warn line, I would simply never put the dashboard behind a reverse proxy, so it stays a warning I accept.
Then the part that is not about your setup. Three findings from 2026 shaped how I treat OpenClaw, and I only list the ones I could trace to a primary report:
- Malicious skills on ClawHub. Koi Security audited 2,857 skills on the marketplace and found 341 malicious ones, in a campaign they called ClawHavoc; 335 of them used fake prerequisites to install the Atomic Stealer macOS malware. Publishing to ClawHub needed only a week-old GitHub account. OpenClaw added skill reporting and auto-hiding in response (The Hacker News, 2 February 2026).
- Skills that bypassed screening. Palo Alto Networks' Unit 42 reported five malicious skills that got past the marketplace's checks; OpenClaw removed them and banned the accounts.
- ClawJacked. Oasis Security showed that JavaScript on any web page could open a WebSocket to the gateway on localhost and brute-force its password at hundreds of guesses a second, because the rate limiter exempted localhost and localhost pairings were auto-approved. Fixed in 2026.2.25, within a day of the report; the team rated it high severity.
There are larger numbers in circulation, including an audit figure in the hundreds of findings; I could not trace that one to a primary source, so I do not repeat it. What I take from the three above is simple: run a current version, install skills the way you would install a browser extension from a stranger, which is to say rarely and after reading the code, and keep the gateway where only you can reach it.
What I would not do
- Run it as my own user on the machine I work on. It gets a user, or a machine.
- Expose the gateway to the internet to reach the dashboard from my phone. Telegram is the phone interface.
- Install a skill because the agent suggested it. Skills are code from strangers with the agent's permissions.
- Give it a hosted model and my inbox on day one. Local model, read-only sources, one scheduled job, then earn the rest.
Sources
- OpenClaw docs, Install (accessed October 20, 2026)
- OpenClaw docs, Ollama setup (accessed October 20, 2026)
- OpenClaw docs, Ollama provider overview (accessed October 20, 2026)
- OpenClaw docs, Gateway security (accessed October 20, 2026)
- OpenClaw docs, Telegram setup (accessed October 20, 2026)
- OpenClaw docs, Automations: schedules (accessed October 20, 2026)
- OpenClaw docs, Automations: payloads (accessed October 20, 2026)
- The Hacker News, Researchers find 341 malicious ClawHub skills stealing data from OpenClaw users (accessed October 20, 2026)
- Oasis Security, OpenClaw vulnerability: website-to-local agent takeover (ClawJacked) (accessed October 20, 2026)
- Mallory (reporting Unit 42), Malicious OpenClaw skills bypassed ClawHub screening (accessed October 20, 2026)
- GitHub, openclaw/openclaw (accessed October 20, 2026)
Written by Lera Leonteva. Install and run outputs are from a Node 26 container on 21 October 2026; the Telegram step was not completed.