< back to series
DAY 4

First Security Review and the Credential Problem

Mar 21, 2026

The v1 architecture

At the end of day 3:

  • tito runs as a non-root OS user with a sudo allowlist
  • All API credentials live in /etc/environment.d/openclaw.conf
  • No inbound ports. Telegram uses outbound long-polling
  • Cloudflare access split: D1/R2 via read-only tokens, CRM writes via Worker gated by Cloudflare Access

The initial setup had these constraints: no inbound ports, bounded sudo, read-only D1 and R2 access, per-repo SSH keys at chmod 600, and CRM writes gated by two separate credentials.

The credential problem

All API tokens sat in tito's environment. The agent ran as tito. If the model got tricked, say by prompt injection in fetched content, into printing environment variables, it would find real keys. Markdown rules saying "don't share credentials" are not a control. They are just words the model can be talked around.

The D1 token has a second problem: it's scoped at the account level, not per-database.

The Telegram bot token sits in the URL path (/bot<token>/...), not as an HTTP header, so header-level injection doesn't cover it.

Proposed fix

Two changes came out of the review:

  1. Secrets proxy under a separate OS user. Real tokens held by openclaw-secrets. tito gets placeholder values. All outbound HTTP routes through a proxy that injects real credentials per-host. Compromised prompt gets "proxy-managed", not a real key.

  2. Cloudflare IP allowlist. Pin accepted IPs to the home server so leaked tokens only work from the correct origin.

Neither change was implemented on day 4. Day 8 covers the implementation.