Run tau in Cloudflare (cloud mode)¶
By default tau runs on your own machine and keeps your life data there (local mode, ADR 0006). Cloud mode runs the same tau without a machine at home. The hub runs as a container in your own Cloudflare account, next to your spine Worker, and you talk to it through Telegram. The decision is ADR 0007.
What you trade
In cloud mode your sessions, persona (including persona/user.md) and settings live in your Cloudflare account: D1 and KV, under Cloudflare's own at-rest encryption, not a key only you hold. Anyone who gets into that account, or a leaked HUB_TOKEN, can read them. Local mode keeps all of this on your machine. Pick cloud mode only when you accept that.
What runs where¶
| Piece | Where |
|---|---|
The spine Worker (tau-spine) |
Cloudflare Workers: your address, card and mailbox, the store API, a cron |
The brain (tau hub run, the same Python code) |
a Cloudflare Container managed by the spine, started with tau cloud run |
| Sessions, small state (contacts, cursors) | D1, through the spine ([sessions] backend = "spine") |
tau.toml, persona/*.md |
KV, uploaded with tau cloud push |
| Credentials (model keys, Telegram token, network identity) | Worker secrets, handed to the container as environment variables |
Before you start¶
- A Cloudflare account on Workers Paid (about $5/month), because Containers need it. The container itself is billed for the time it runs; the spine's cron keeps it awake so the Telegram poller can run.
- Docker running on the machine you deploy from, because Wrangler builds the image locally.
- Node.js 20+ and pnpm, and a clone of this repo.
- A working local setup first (
tau init,tau doctor), because cloud mode uploads yourtau.tomland persona.
1. Configure and deploy the spine in cloud mode¶
cd cloud/spine
pnpm install
pnpm run configure --cloud --force # wrangler.jsonc from wrangler.cloud.example.jsonc
pnpm exec wrangler login
Open cloud/spine/wrangler.jsonc and set SPINE_URL to the URL your spine will have: the workers.dev URL, or your custom domain if you add a route. The container uses it to reach the spine. Then deploy:
2. Set the secrets¶
Every value below goes in with wrangler secret put <NAME>, which prompts for it. Never pass a secret as a command argument.
| Secret | Value |
|---|---|
HUB_TOKEN |
a long random value; your local tau cloud commands use it as TAU_SPINE_TOKEN |
ANTHROPIC_API_KEY (and ANTHROPIC_WORKSPACE_ID if needed), or AWS_REGION + AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY / AWS_BEARER_TOKEN_BEDROCK |
the model credentials your tau.toml roles need |
TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_CHAT_IDS |
as in the Telegram guide |
TAU_NET_IDENTITY |
your network identity, if the cloud tau should join the tau network: base64url of the JSON in TAU_DATA_DIR/net/identity.json (Join the tau network) |
The brain enforces the budget tables of your tau.toml itself, exactly as a local hub does (Models and budgets), but its spend ledger sits on the container's disk, which is lost whenever the container stops, so after a restart every budget counts from zero again; keep the monthly limit in your provider's console.
3. Upload your configuration and persona¶
On the machine that has your tau.toml and persona/, put TAU_SPINE_URL (the spine URL) and TAU_SPINE_TOKEN (the HUB_TOKEN) in .env, then:
tau cloud push # tau.toml, persona.md, net.md; shows the list and asks first
tau cloud status # spine health and mode, the files it holds, the brain container's health
- The file set is exactly
tau.toml,persona/persona.md,persona/user.mdandpersona/net.md. Nothing else is read or sent, and.envis never uploaded. persona/user.mdholds what tau knows about you, so it has its own question, and the default answer is no.tau cloud push --include-useruploads it without the question.- In a script (no terminal),
tau cloud push --yesuploads without asking;persona/user.mdthen goes up only with--include-useras well. - The brain reads these files when it starts. After a push, the running brain keeps the old ones until its next start (a redeploy restarts it).
tau cloud pulldownloads the same files, for example onto a new machine. It never replaces a local file that differs unless you pass--force.
4. Talk to it¶
Write to your bot on Telegram. The container's hub answers, and tier-2 approvals arrive as inline buttons exactly as in local mode. Sessions are stored in D1, so they survive a container restart. /resume works as usual.
What the brain container runs¶
The image (cloud/spine/container/Dockerfile) starts tau cloud run, which:
- checks that
TAU_SPINE_URLandTAU_SPINE_TOKENare set and that the spine runs in cloud mode (otherwise it exits with one line); - pulls the files into
TAU_HOMEand changes two keys of the pulledtau.toml:[sessions] backend = "spine"and[component.net] store = "spine". Everything else, comments included, stays as you wrote it; - answers
GET /healthon port 8080 with{"ok": true, "version": ..., "hub": <the hub's /status or null>}, which the spine'sGET /hub/brainpasses on; - runs
tau hub runin the same process, until the container runtime sends SIGTERM, which stops the hub cleanly.
tau cloud run refuses a TAU_HOME that holds a tau.toml it did not write, so it never overwrites your own configuration.
Try it locally, without a container¶
pnpm run dev:cloud runs the spine in cloud mode with containers off, so you can check the files and the spine session backend without Docker or a Cloudflare account:
(cd cloud/spine && pnpm install && pnpm run configure && pnpm run dev:cloud)
# another terminal; TAU_SPINE_URL=http://127.0.0.1:8787 and TAU_SPINE_TOKEN from cloud/spine/.dev.vars in .env
tau cloud push && tau cloud status # the brain shows as unavailable: containers are off
# play the container: the spine variables in the environment, an empty TAU_HOME and TAU_DATA_DIR,
# and another hub port in case your own hub runs on 7877
export TAU_SPINE_URL=http://127.0.0.1:8787 TAU_SPINE_TOKEN=<HUB_TOKEN from cloud/spine/.dev.vars>
export TAU_HOME=/tmp/brain/tau TAU_DATA_DIR=/tmp/brain/data TAU_HUB_PORT=7977
TAU_CLOUD_HEALTH_HOST=127.0.0.1 tau cloud run
curl http://127.0.0.1:8080/health # a third terminal
A tau chat with the same variables then stores its session in the local spine's D1.
Going back to local mode¶
Deploy the local-mode configuration again (pnpm run configure --force && pnpm run deploy), with a deleted_classes migration for TauBrain added to its migrations list. cloud/spine/README.md shows the exact list. The container stops and your address stays. Contacts, the network log and sessions stay in the spine's D1, but a local-mode spine never serves them, so a local hub starts with an empty contact list. There is no tool to move them yet (issue 108). The data stored in D1 and KV stays in your account until you delete it (wrangler d1 execute, wrangler kv key delete, or delete the resources in the dashboard).