TAB 2 β€” THE FULL BUILD

From zero to a personalised AI agent.

Seven phases, each driven by a single Claude Code prompt. The lived-experience playbook.
3–4 hrs
Build time
$25–60
Monthly cost
7
Phases
This is the lived-experience version of the build. Every phase is a single prompt you send to Claude Code, and Claude Code does the work while you approve each step. You don't need to know Linux commands β€” you need to be able to read what Claude Code proposes and approve or push back.

This tab is written for the single-agent build (one OpenClaw instance). If you want the dual-agent version, toggle the β€œMulti-agent” switch at the top of the guide. Most readers should start with single-agent.

All prices shown in AUD. Use the currency selector at the top to change.

Before you start

Overview

What you're building: A single OpenClaw AI agent running on a hardened cloud server, accessible via Telegram (and optionally Discord), with personalised identity files that teach it who you are and how you work, backed up to a private GitHub repo.

text
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              You                      β”‚
β”‚   Telegram DMs  Β·  Optional Discord  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
      β”‚   Agent     β”‚
      β”‚  (OpenClaw) β”‚
      β”‚  Port 8080  β”‚
      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
             β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚    Hetzner VPS (agent-01)    β”‚
      β”‚   CAX21 Β· ARM64 Β· Ubuntu 24  β”‚
      β”‚    Helsinki Β· ~$18 AUD/mo    β”‚
      β”‚   Cloudflare Tunnel (opt.)   β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Total time: ~3–4 hours of focused work

Total cost: ~$18 AUD/month server + ~$8–40 AUD/month Claude API (single agent)

The build in 7 phases

Each phase is a single Claude Code prompt. Copy-paste, approve each step as Claude Code proposes it, verify the checkpoint, move to the next phase.

PhaseWhatTime
1Server foundation + SSH lockdown15–30 min
2System hardening + runtime dependencies30–45 min
3Cloudflare Tunnel (secure remote access)20–30 min
4Agent install + first test30–45 min
5Identity (SOUL.md) + personalisation~1 hour
6Channels (Telegram primary, Discord optional)15–30 min
7Backup to GitHub + cleanup5–10 min

Phase 5 is the longest because the creative work β€” writing who you are and how your agent should behave β€” takes real thinking time. Everything else is execution.

Pre-build: Accounts and credentials (30 minutes of admin)

Before Phase 1, create these accounts and save all credentials in a password manager (or notes app) with a tag/folder like agent-board:

AccountWhereWhat you'll get
Hetzner Cloudconsole.hetzner.cloudServer hosting (payment method required)
Anthropicconsole.anthropic.comAPI key for the agent
TelegramBotFather on TelegramBot token + your numeric user ID
Cloudflaredash.cloudflare.comDomain for tunnel (free tier is fine)
GitHubgithub.comPrivate repo for backup
(Optional) Discorddiscord.com/developersBot token for a second communication channel

SSH key: Check if you already have one:

bash
ls -la ~/.ssh/id_ed25519.pub

If not, generate one:

bash
ssh-keygen -t ed25519 -C "your-email@example.com"

Accept all the defaults. You'll paste the public key (~/.ssh/id_ed25519.pub) into Hetzner during server creation.

Phase 1: Server Foundation + SSH Lockdown (15–30 min)

Step 1a: Provision the server (manual, Hetzner web console)

Do this manually in the Hetzner web console:

SettingValue
LocationHelsinki (or nearest available in Europe)
ImageUbuntu 24.04
TypeCAX21 (ARM64, 4 vCPU, 8GB RAM)
SSH KeyAdd your public key (~/.ssh/id_ed25519.pub)
BackupsEnable (+20%, worth it)
Nameagent-01 (or whatever you prefer)

Total cost: ~$18 AUD/month

Firewall setup (Hetzner Cloud Firewall):

  • SSH (TCP 22) from your home IP only β€” run curl -4 ifconfig.me on your Mac to find it, append /32
  • ICMP from anywhere (for ping debugging)
  • Apply to your server
β„Ή
Hetzner UI note

The Source IPs field shows as β€œAny IPv4” / β€œAny IPv6” chips. Click them to delete the defaults, then type your IP with /32 and press Enter to add it as a new chip.

After creation, save the IP address in your password manager.

Step 1b: First SSH + create a non-root user (safety-net pattern)

This is the one step to do manually, not via Claude Code. The cost of getting locked out is too high.

Open Terminal Window 1 and connect:

bash
ssh root@YOUR_IP_ADDRESS

Type yes to accept the host key.

Create a non-root user with sudo access. Replace <your-user> with whatever username you want (e.g. agent, admin, your first name):

bash
adduser --gecos "" <your-user>
# Set a password when prompted β€” save it in your password manager
usermod -aG sudo <your-user>
mkdir -p /home/<your-user>/.ssh
cp /root/.ssh/authorized_keys /home/<your-user>/.ssh/authorized_keys
chown -R <your-user>:<your-user> /home/<your-user>/.ssh
chmod 700 /home/<your-user>/.ssh
chmod 600 /home/<your-user>/.ssh/authorized_keys

Open Terminal Window 2 (this is the safety-net pattern). Test that you can SSH in as the new user:

bash
ssh <your-user>@YOUR_IP_ADDRESS
sudo whoami  # should print "root" after entering your password

If Window 2 works, keep Window 1 open as your safety net and proceed with the SSH lockdown in Window 1.

In Window 1 (still as root), edit the SSH config:

bash
nano /etc/ssh/sshd_config

Change these settings (use Ctrl+W to search for each one):

text
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no
MaxAuthTries 3
AllowUsers <your-user>

Save with Ctrl+O, Enter, Ctrl+X.

Restart SSH:

bash
systemctl restart ssh

Open a third terminal window and verify the lockdown worked:

bash
ssh root@YOUR_IP_ADDRESS  # should FAIL with "Permission denied" β€” this is what you want
ssh <your-user>@YOUR_IP_ADDRESS  # should succeed

If both tests pass, close Window 1 (root) and Window 3. Window 2 (<your-user>) is now your working session.

Step 1c: Set up SSH config shortcut on your Mac (optional but useful)

On your Mac (not the server), edit ~/.ssh/config:

bash
nano ~/.ssh/config

Add:

text
Host agent-01
    HostName YOUR_IP_ADDRESS
    User <your-user>

Now ssh agent-01 is shorthand for the full command.

βœ“
Phase 1 complete

You have a hardened server that only your non-root user can SSH into, only from your home IP, using only your SSH key.

Phase 2: System Hardening + Runtime Dependencies (30–45 min)

This is where Claude Code takes over. From here, you're driving with prompts, not commands.

Install Claude Code on the server

SSH in as your user and install Claude Code:

bash
ssh agent-01
curl -fsSL https://claude.ai/install.sh | bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
claude --version  # verify

Add temporary passwordless sudo so Claude Code can run commands without getting stuck on password prompts:

bash
echo "<your-user> ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/<your-user>-nopasswd
sudo chmod 440 /etc/sudoers.d/<your-user>-nopasswd

We'll remove this in Phase 7.

Create a working directory and start Claude Code:

bash
sudo mkdir -p /opt/agent-board
sudo chown <your-user>:<your-user> /opt/agent-board
cd /opt/agent-board
claude

Authenticate via the browser OAuth flow when prompted.

Phase 2 prompt (send this to Claude Code)

Copy-paste this entire prompt as your first message to Claude Code:

β–Ά Claude Code Prompt
I need you to harden this Ubuntu 24.04 server and install all runtime dependencies needed to run an OpenClaw AI agent. Please do the following as a single phase with grouped approvals where sensible. **Hardening:** 1. Update and upgrade all packages using `sudo DEBIAN_FRONTEND=noninteractive apt upgrade -y -o Dpkg::Options::="--force-confold"` (this prevents debconf dialogs from stalling the process) 2. Set hostname to `agent-01`, timezone to the appropriate timezone for the user (ask me if unclear) 3. Create a 2GB swap file with sensible swappiness settings 4. Install and configure UFW (host firewall): allow SSH on port 22 FIRST, then enable the firewall. The order matters β€” if you enable UFW before allowing port 22, I lose my SSH session. 5. Install and enable fail2ban with sensible defaults 6. Install and configure unattended-upgrades for automatic security patches 7. Install auditd for audit logging 8. Disable snapd (not needed, just overhead) **Runtime dependencies:** 9. Install Node.js 24 from NodeSource (NOT Node 20 β€” OpenClaw requires 24+) 10. Install pnpm globally via npm 11. Install Docker using the official APT repository method (not the convenience script) β€” use `/etc/apt/keyrings` for the GPG key and add the `deb` entry for the current Ubuntu codename 12. Add my user to the `docker` group so I can run docker without sudo **Rules:** - ALWAYS use `DEBIAN_FRONTEND=noninteractive` and `-o Dpkg::Options::="--force-confold"` for apt commands - Show me each command before running anything that modifies system state - Group related commands into single approval requests where sensible (e.g. "install + configure + enable fail2ban" as one approval) - The UFW step is the most dangerous β€” show it as its own approval and double-check the ordering with me before running it - When something fails, read the error, try the obvious fix once, then ask me before looping After all steps complete, verify with: - `node --version` (should show v24.x.x) - `docker --version` - `sudo ufw status` (should show active with port 22 allowed) - `sudo systemctl status fail2ban` Stop at the end and summarise what was done.
⚠
What to watch for

The UFW step β€” make sure Claude Code runs sudo ufw allow 22/tcp before sudo ufw enable. If it's in the wrong order, say β€œstop, that's the wrong order” before approving.

Any apt command that doesn't have DEBIAN_FRONTEND=noninteractive β€” push back and ask Claude Code to include it.

Phase 2 complete when Claude Code confirms all verification commands pass.

Phase 3: Cloudflare Tunnel (20–30 min)

Cloudflare Tunnel gives you secure remote access to your server without opening ports. You'll access your agent via subdomains like agent.yourdomain.com without exposing anything to the public internet.

Prerequisite: A domain managed by Cloudflare (free tier is fine). If you don't have one, you can skip this phase entirely β€” the agent will still work via Telegram, you just won't have a web interface for direct API access.

Phase 3 prompt

β–Ά Claude Code Prompt
I want to set up a Cloudflare Tunnel so I can securely access this server without opening any ports. My Cloudflare-managed domain is: `YOUR_DOMAIN_HERE`. Please do the following: 1. Install the cloudflared package (official Cloudflare binary for ARM64) 2. Guide me through the browser-based `cloudflared tunnel login` flow β€” you can't do this yourself, so pause and tell me exactly what to click 3. Create a new tunnel called `agent-01` 4. Create the ingress config at `/etc/cloudflared/config.yml` with the following routes: - `agent.YOUR_DOMAIN_HERE` β†’ `http://localhost:8080` - A catch-all returning HTTP 404 5. Route DNS for the subdomain via `cloudflared tunnel route dns` 6. Install cloudflared as a systemd service so it starts on boot 7. Start the service and verify it's running Also create a Cloudflare Access application for `agent.YOUR_DOMAIN_HERE` with email-based authentication (my email only β€” I'll tell you what it is). Show me each command before running. Pause for any browser OAuth flows and tell me what to click.
β„Ή
What to watch for

The cloudflared tunnel login step requires you to click a URL in your browser and authorise the domain. Claude Code will pause and tell you what to do.

DNS propagation takes 1–2 minutes after routing is set up. Test with curl https://agent.YOUR_DOMAIN_HERE and expect a 404 (because the agent isn't running yet β€” that's correct).

Skip this phase if you don't have a Cloudflare-managed domain. The agent will work fine via Telegram alone.

Phase 4: Agent Install + First Test (30–45 min)

This is where the agent actually comes to life. You'll install OpenClaw, configure it, and test that it can respond via Telegram.

Phase 4 prompt

β–Ά Claude Code Prompt
I need you to install and configure OpenClaw as a system-level service on this server. This is the core of the build. **Credentials I'll provide:** - My Anthropic API key: [I'll paste it when you ask] - My Telegram bot token: [I'll paste it when you ask] - My Telegram user ID: [I'll paste it when you ask] **Please do the following:** 1. Create a system user `openclaw` with home directory `/home/openclaw` 2. Install OpenClaw globally via pnpm under the openclaw user: `sudo -u openclaw bash -c "pnpm add -g @openclaw/cli"` 3. Create `/etc/agent-board/shared.env` owned by `root:agents` with mode 640 (create the `agents` group first if it doesn't exist, add `openclaw` to it). This file will contain `ANTHROPIC_API_KEY=...` 4. Initialise OpenClaw config as the openclaw user: `sudo -u openclaw bash -c "openclaw init"` 5. Edit `/home/openclaw/.openclaw/openclaw.json` to configure: - `gateway.mode = "local"` - `channels.telegram.enabled = true` - `channels.telegram.token` from env var `TELEGRAM_BOT_TOKEN` - `channels.telegram.dmPolicy = "allowlist"` - `channels.telegram.allowFrom = ["MY_TELEGRAM_USER_ID"]` - `models.default = "claude-sonnet-4-6"` 6. Create `/home/openclaw/.openclaw/.env` with `TELEGRAM_BOT_TOKEN=...` (I'll paste the value) 7. Create a system-level systemd unit at `/etc/systemd/system/openclaw.service` with: - User=openclaw, Group=openclaw, SupplementaryGroups=agents - EnvironmentFile for both `/etc/agent-board/shared.env` and `/home/openclaw/.openclaw/.env` - Restart=always, RestartSec=10 8. Reload systemd, enable the service, start it, and show me the status **Important gotchas to know upfront:** - Use **system-level** systemd, NOT user-level. User-level services can't read files owned by `root:agents` even with group membership. - The `SupplementaryGroups=agents` directive is what gives the service read access to the shared env file. - If the service starts but can't find the API key, check that both EnvironmentFile paths exist and have the correct ownership. After the service is running, verify with: - `sudo systemctl status openclaw` - `sudo journalctl -u openclaw --since "2 minutes ago" --no-pager` (look for "Telegram provider connected") Stop after verification and tell me to test by sending a message to my bot on Telegram.

Testing the agent

Once Claude Code confirms the service is running, open Telegram on your phone, find your bot (search for the username you created), and send:

Hey, what can you do?

You should get a response within 3–5 seconds. If you do: Phase 4 complete. The agent is alive.

If it doesn't respond

Tell Claude Code:

β–Ά Claude Code Prompt
The Telegram bot isn't responding. Please check the service logs for the last 5 minutes, diagnose the issue, and show me the fix before applying it.

Common issues Claude Code will catch:

  1. Wrong API key (no credits loaded, or typo)
  2. Wrong Telegram user ID in the allowlist
  3. Service can't read the shared.env (permissions issue)
  4. OpenClaw defaulting to a wrong base_url

Each of these has a quick fix that Claude Code can apply with your approval.

Phase 5: Identity (SOUL.md) β€” The Most Important Phase (~1 hour)

This is where the agent stops being a generic chatbot and becomes yours. The identity files teach it who you are, how you work, what rules to follow, and what voice to use.

This phase is mostly creative work, not execution. You're writing content, not running commands. Claude Code's job in this phase is to help you write the files, review them, and deploy them.

What SOUL.md contains

A complete SOUL.md typically has these sections:

  1. Mission β€” what this agent exists to do for you
  2. Who you are β€” your name, role, context, life situation
  3. Active priorities β€” what you're working on right now
  4. How you work β€” communication style, pacing, preferences
  5. Hard rules β€” things the agent must never do (spending money, posting publicly without approval, sending emails without review)
  6. Voice & attitude β€” how the agent should speak (direct, no corporate filler, opinionated when asked)
  7. Domain knowledge references β€” pointers to context files for specific projects

The important thing is that SOUL.md is written in your voice, for your life, not a generic template.

Phase 5 prompt (content writing)

β–Ά Claude Code Prompt
I'm going to write my SOUL.md file, which is the identity document for my OpenClaw agent. I'd like your help structuring it and giving me feedback. Please ask me a series of questions to build up the content, one section at a time. Start with the mission (what I want this agent to do for me), then move through who I am, my active priorities, how I like to work, hard rules the agent must follow, and the voice/attitude I want the agent to use. After each section, show me what you've drafted based on my answers and let me refine it before moving on. Keep the tone direct and specific. No corporate filler. Australian English. Write as if the agent will read this every session and use it to decide how to behave.

Work through this conversationally with Claude Code. It takes about 45–60 minutes of real thinking, but the output is the most valuable thing in the entire build.

Phase 5 deployment prompt (after content is written)

β–Ά Claude Code Prompt
I've finished writing my SOUL.md content. Now I need to deploy it to OpenClaw. OpenClaw uses a multi-file template system in `~/.openclaw/workspace/` β€” NOT a single SOUL.md at the root. The files it reads are: - `workspace/SOUL.md` β€” agent personality, values, voice - `workspace/USER.md` β€” information about me (the human) - `workspace/IDENTITY.md` β€” agent's name, creature type, vibe - `workspace/AGENTS.md` β€” operational manual, hard rules, memory system instructions Please help me split my content across these four files appropriately: - Personality/voice content β†’ workspace/SOUL.md - About-me content β†’ workspace/USER.md - Agent name and display metadata β†’ workspace/IDENTITY.md - Hard rules, decision authority, operational behaviour β†’ merge into workspace/AGENTS.md (the default AGENTS.md has good operational content β€” preserve it and add my rules) Show me each file before writing it so I can review. After deployment, restart the OpenClaw service and we'll test.

Testing identity loaded correctly

After deployment and service restart, send your bot a knowledge-question test on Telegram:

What do you know about me and what are you here to do?

If you get a specific, personalised response β€” identity loaded correctly. Phase 5 complete.

If you get a generic β€œI'm your AI assistant” response β€” identity didn't load. Tell Claude Code:

β–Ά Claude Code Prompt
My agent isn't loading my identity files. When I ask "what do you know about me," I get a generic response. Please diagnose: 1. Verify the files exist at `/home/openclaw/.openclaw/workspace/` with the correct ownership 2. Check the OpenClaw logs for any file loading errors 3. Verify OpenClaw is actually reading from the workspace directory (not from `/home/openclaw/.openclaw/` root) 4. Restart the service and try again Show me what you find before making changes.

Phase 6: Channels β€” Telegram Primary, Discord Optional (15–30 min)

Telegram (required β€” already set up in Phase 4)

Your agent is already responding on Telegram from Phase 4. A few polish items via BotFather:

  1. Open Telegram, message @BotFather
  2. /setname β€” set a friendly display name for your bot
  3. /setuserpic β€” upload an avatar
  4. /setdescription β€” one-sentence description
  5. /setprivacy β†’ Disable β€” required if you ever want the bot to see messages in group chats

That's it for Telegram.

Phase 7: Backup + Cleanup (5–10 min)

Phase 7 prompt

β–Ά Claude Code Prompt
Please finalise the build with these cleanup tasks: 1. Initialise `/opt/agent-board` as a git repo 2. Create a `.gitignore` that excludes all .env files, paired.json device files, any SQLite databases, node_modules, and caches 3. Copy the key config files into `/opt/agent-board/` for version control: - `/home/openclaw/.openclaw/openclaw.json` (secrets stripped) - `/home/openclaw/.openclaw/workspace/SOUL.md`, USER.md, IDENTITY.md, AGENTS.md - `/etc/systemd/system/openclaw.service` 4. Create a README.md in `/opt/agent-board` describing the setup 5. Make an initial commit 6. Remove the temporary passwordless sudo file at `/etc/sudoers.d/<your-user>-nopasswd` 7. Verify sudo still works (will now prompt for password) After this is done, remind me to: - Create a private GitHub repo and push `/opt/agent-board` to it - Set up an SSH deploy key for ongoing auto-sync (we can do this later)

Create the GitHub repo manually

  1. Go to github.com β†’ New repository
  2. Name it agent-board (or whatever you prefer)
  3. Set it to Private
  4. Don't initialise with a README (we already have one)
  5. Follow GitHub's β€œpush an existing repository” instructions
βœ“
Phase 7 complete

Your build is backed up and the build-session passwordless sudo is removed.

You're done

You now have:

  • A hardened cloud server in Europe, invisible to the public internet
  • A personalised AI agent with your identity, your rules, your voice
  • Accessible via Telegram from anywhere
  • (Optional) Cloudflare Tunnel for secure web access
  • (Optional) Discord as a second channel
  • Backed up to a private GitHub repo

Total time: 3–4 focused hours (vs the two full days our original build took β€” because you benefited from the lessons we learned the hard way).

What ate the time in our original build (so it won't eat yours)

Our first build took ~10 hours across two days because we were discovering problems as we went. These are the specific things that cost us time, all of which this guide prevents:

ProblemTime costHow this guide prevents it
openssh-server debconf stall on apt upgrade30 minPhase 2 prompt includes DEBIAN_FRONTEND=noninteractive from the start
Node 20 β†’ 24 upgrade after install failure20 minPhase 2 prompt specifies Node 24 directly
SOUL.md file structure confusion (single vs workspace/)60 minPhase 5 prompt explains the multi-file template system upfront
Discovering OGP federation doesn't exist30 minNot mentioned anywhere in this guide
Telegram bot-to-bot limitation45 minNot a concern for single-agent builds
Hermes defaulting to OpenRouter base_url20 minNot applicable for OpenClaw-only build
Device pairing loop after rapid restarts30 minGuide recommends one clean restart after all config changes
Sudo password prompts stalling Claude Code15 minGuide sets up temporary passwordless sudo from the start

That's ~4 hours of troubleshooting we did so you don't have to.

Next steps

Now that you have a working personalised agent:

  • Use it for a week. See what you actually reach for it to do. Let the real use cases emerge before adding capabilities.
  • Tab 5 (Troubleshooting) β€” bookmark this tab for when things break
  • Tab 4 (Using Claude as Copilot) β€” read this to understand why the copilot pattern worked and how to use it for future projects
  • Consider dual-agent β€” if you find yourself wishing for a second, more specialised agent, toggle the multi-agent switch at the top of the guide and revisit Phase 4

The agent board only becomes valuable when you treat it like one. Daily use, weekly review, honest feedback to the agent when it gets something wrong. Three months of that and you'll have an agent that genuinely knows you better than most colleagues.