A field guide · October 2026 · read the post

Cora 🪸

An AI familiar that lives on a spare MacBook Air in my house, talks to me on Telegram, and quietly turns my email, photos, voice memos and whereabouts into a written record of our life.

OpenClaw 2026.9.8 Host MacBook Air M2 15″, 16 GB Uptime never sleeps Brain ChatGPT plan + 2 metered fallbacks Since May 8, 2026
Building your own: skip to the end, copy the Claude Code prompt, and let a Claude Code session do the setup. Don't run the steps by hand.
Everyone else: start with what she does all day and the decisions that made it work.

Primer

The pieces, in plain English.

Here's every tool this guide mentions, what it is, and how I use it. Each one has a tag for anyone building their own: get it, build your own or optional.

OpenClaw

Get it

Open-source software that turns an AI model into an assistant that lives in your chat apps.

It runs as a background service on one computer. It connects a model to Telegram, gives it tools (files, shell, web search, other services), remembers things in plain text files, and runs jobs on a schedule.

Cora is an OpenClaw agent. The model does the thinking, and OpenClaw gives it a home, a phone number and an alarm clock.

Codex & ChatGPT

Get it

OpenAI's subscription and its coding agent. This is the account that powers Cora.

OpenAI lets you sign in to OpenClaw with a ChatGPT plan, so Cora's thinking comes out of a flat monthly subscription instead of a per-token bill. Anthropic doesn't allow a Claude subscription to power OpenClaw, which is why Cora isn't on Claude.

Cora runs on the ChatGPT plan, with metered API keys only as a backup. I also use Codex as a second reviewer on code that Claude writes.

Claude Code

Get it

Anthropic's AI agent that works in your terminal: it reads files, runs commands, writes code and fixes what breaks.

Claude Code is awesome at managing things. I don't edit OpenClaw config by hand. Claude Code installs it, upgrades it, writes Cora's scripts, chases down why a job failed, and backs up the config before every change.

Claude Code runs on my Claude subscription, which is allowed because it's Anthropic's own tool. Claude Code maintains Cora, and ChatGPT runs her.

Git & GitHub

Get it

Git keeps the full history of a folder of files. GitHub stores that history online and adds a place to plan and review changes.

Getting a GitHub account early is one of the smartest moves in this whole setup. An agent's brain is a folder of text files, so with git every change an agent makes becomes a commit you can read and undo. It's also an off-machine backup: if the Mac dies, you clone the repo onto a new one and you're back.

Claude Code works naturally in repos, issues and pull requests. Cora's workspace, my vault and all my projects are git repos, and every agent edit has a commit message saying who changed what and why.

AWS

Get it

Amazon's cloud: storage, servers, email sending, databases, and just about anything else, billed by use.

Having an AWS account ready is great, because when you need something (backups, a small server, a website, sending email) an agent can set it up in minutes. Cora started out on a $12/month AWS Lightsail server.

I use it for backups and for hosting whatever my projects need. Set a billing alert on day one so a runaway experiment can't surprise you.

Cotrugli

Build your own

My personal data lake: photos, documents, messages, location, health and money records, all in one searchable place that agents can query.

I built it, and it's the reason Cora knows so much context. It's super personal, so it isn't something I share, and I don't have an off-the-shelf equivalent to recommend. The table further down lists what it does, which doubles as the list of jobs you'd give your own agent.

Cora searches it, files tasks in it, and uploads photos to it before linking them from notes.

Obsidian

Optional

A notes app that reads a plain folder of Markdown files. My “vault” is that folder, kept in git.

Obsidian is becoming less and less important to me. It's a nice viewer and an occasional manual editor, but I hardly open it. Cora and Claude Code write straight into the folder, and I mostly read the results through them.

If you want somewhere to browse what your agent writes, it's free and pleasant. You don't need it.

Telegram

Get it

The chat app I use to talk to Cora.

Making a bot is free and takes two minutes, bots work in DMs and groups, and the phone app makes Cora feel like texting a person.

OpenClaw also supports Discord, Slack and others if you'd rather live there.

MCP shows up everywhere below. It's a standard plug that lets an agent use another service's tools. Cotrugli and my heat pumps each expose an MCP server, and Cora plugs into both.

The wiring

One gateway, one bot, everything local.

OpenClaw runs a single gateway process under launchd. Telegram is the only front door. Because the gateway sits on a real Mac instead of a cloud VM, Cora can read the things that only exist on Apple hardware: Messages, Photos, Voice Memos, Reminders and Contacts.

In

  • Telegram DMs (allowlist: me)
  • A family group chat
  • A friends group chat
  • Heartbeat every 30 min
  • ~20 scheduled jobs
Gateway

localhost only
token auth from a file
launchd keeps it alive
kicked fresh at 3:40 AM

Out

  • Telegram replies, only when worth it
  • Obsidian vault commits, signed “Cora on behalf of John”
  • Cotrugli tasks and artifacts
  • ESPN fantasy moves (judged)

Models

  • gpt-6-sol on my ChatGPT plan (OAuth)
  • falls back to metered gpt-5.6-luna
  • then metered Claude Haiku 4.5
  • cheap luna pinned on high-volume jobs

MCP servers

  • Cotrugli: my personal data lake
  • Mitsubishi HVAC (6 tools only)
  • Tidbits · Brave search

Local Mac sources

  • Messages chat.db + Contacts
  • Photos library
  • Voice Memos
  • Gmail & Calendar via gog
  • Apple Reminders

A day in Cora's life

What she does while I'm not looking.

Times Pacific. Plus weekly: skill-collection review, and a Tuesday 6 AM job where Cora upgrades her own fantasy-sports CLI and verifies it with doctor --live. Almost every run ends in NO_REPLY, so I only hear about it when something changed.

The fun part

Things I didn't expect an agent to be good at.

She dreams, and keeps a diary

OpenClaw's memory-core plugin runs a “dreaming” pass at 3:00 AM that promotes the day's short-term notes into long-term memory. Side effect: a 2,200-line DREAMS.md of surreal, oddly tender diary entries, often ending in a haiku.

“a cron job wearing slippers”

Our life, written down automatically

Every 30 minutes she reads new email, texts, photos, voice memos and my phone's location track, and writes what matters into my Obsidian vault: daily notes, people files, trips, purchases, a health log. Photos get uploaded to my artifact lake first so the vault links to the real bytes. Cora transcribes voice memos on the Mac, starting with Apple's own transcript and falling back to local models.

Fantasy football on autopilot, with a judge

She watches my three ESPN leagues and FantasyPros news every heartbeat. For routine moves (benching an OUT player, a $2 waiver claim) she builds a candidate packet and hands it to a second agent, fantasy-judge, which has no shell, no messaging and no channel. Only a strict-JSON APPROVE for that exact move lets her write, and it expires five minutes after the ESPN snapshot. Stars, trades and big bids come to me.

≤ $2 and ≤ 5% FAAB≤ 2 adds / team / dayno self-approval

A to-do list that tidies itself

Anything I commit to in an email or voice memo becomes a task. Every two hours a silent sweep dedupes, expires stale errands and closes tasks it has evidence I finished. Twice a day I get one digest with up to five numbered offers, and I reply “yes 1” or “drop 3” right in Telegram.

Every agent session lands in the vault

Hooks on OpenClaw, Claude Code and Codex all call one script that files a session note in that day's folder. Whichever agent did the work, there's a record. A design rule I like: integrations must call the script, so the record never depends on an agent remembering to write it.

House and group chats

She can read and set the Mitsubishi heat pumps, and she plays a careful guest in two group chats. Each group has its own system prompt: help freely, but never volunteer anything from my vault, email, health or finances unless I bring it in.

The data lake underneath

What Cotrugli does so Cora doesn't have to.

Cotrugli runs on its own all day: collectors on the Mac push my messages, photos, voice memos, contacts and files to it, and my phone streams location. It processes everything the moment it lands, so when Cora asks “where was I Tuesday?” or “what did that pharmacy label say?” the answer is already sitting there. If you don't have a data lake, this is the list of jobs you'd hand your agent instead, one scheduled script at a time.

Location

Takes a live feed from the phone (Overland or OwnTracks), keeps a separate track for each person, works out where we stayed and for how long, and gives untagged photos a location from the track.

Poll a location app herself, store the points, and figure out visits from raw GPS jitter on every run.

Medications

Checks every photo for medication. A Claude vision model logs confident doses automatically, and anything uncertain waits in a queue for a human to confirm.

Look at every new photo with a vision model and keep her own dose log.

Messages

Copies iMessage every 30 seconds into a searchable history, one conversation per day, with embeddings for meaning-based search.

Read the Messages database directly and search it from scratch each time.

Photos & faces

Recognizes faces and assigns them to people, keeps track of who appears with whom, reads text in images, decodes QR codes, and tags what's in each picture.

Run face and text recognition on every photo and remember who's who.

Audio & video

Transcribes recordings and video, with a vocabulary rebuilt every week from the names of our people and medications so they're spelled right.

Transcribe everything herself and fix the names by hand.

People

Turns my contacts into people, links phone numbers and emails to them, seeds face recognition from contact photos, and flags conflicting cards for me to merge.

Match every sender and face to a person on her own.

Documents

Sweeps up screenshots and Downloads, turns PDFs into text, reads the dates in them, and writes a summary and search index for every file.

Open and read each file whenever a question touched it.

Money

Syncs bank and card accounts, parses statement PDFs and matches them to transactions, and matches Amazon orders to the card charges that paid for them.

Parse statements and reconcile accounts by hand.

Health

Pulls Oura sleep and readiness every six hours.

Call the Oura API and keep her own history.

House

Records the heat pumps every five minutes. Photos that catch one of my printed QR labels log where that item was last seen.

Poll the thermostat and track where things are kept.

Tasks & notes

Mirrors my open GitHub issues into the task queue every 30 minutes and resyncs the vault every five, so one search covers code work, life tasks and notes.

Check GitHub and the vault separately on every sweep.

Still in progress: a daily narrative of our life and a small AI newsroom that writes articles from all this data. Cora handles the judgment calls Cotrugli leaves for a human, like confirming a medication or merging two contacts, and the triage jobs on the Mac still decide what's worth writing down.

Key decisions

What I chose, and why.

A spare Mac at home, not a $12 cloud VM

My first agents ran on AWS Lightsail. Moving Cora to a MacBook Air unlocked Messages, Photos, Voice Memos, Reminders and Contacts. Sleep is disabled.

One agent, one bot, one owner

Telegram DMs are allowlisted to my user ID. Groups require an @mention unless I've given that group a named prompt. Only I can run owner commands.

Gateway bound to localhost

No Tailscale, and the gateway listens only on localhost, so nothing outside the Mac can reach it. Risky node commands (camera, screen recording, SMS, adding contacts or events) are denied outright.

Secrets live in files, never in config

Every key is a chmod 600 file in ~/.secrets, referenced from openclaw.json as a file SecretRef. I can paste the config anywhere without leaking a token.

Subscription first, metered as backup

The primary model rides my ChatGPT plan through OAuth. If that fails, the chain drops to a metered OpenAI key, then to Anthropic. A provider outage degrades her; it doesn't silence her.

Scripts fetch, the model judges

Each triage job says “Run exactly: scripts/…py”. The script owns cursors, state and dedupe and emits only what's new. The LLM decides what matters. Cheaper, repeatable, and no re-reporting the same email.

Least-privilege tools per job

Every cron job carries its own tool allowlist. The location job gets three Cotrugli tools and file access, nothing else.

Contracts in docs, with a deployment gate

Long-lived behaviors (tasks, location, fantasy autonomy) live in docs/*-contract.md. Changing one means: update the cron payload, check its tool list, run a live smoke test, then let it loose.

Quiet by default

Heartbeat “OK”s are hidden. Jobs answer NO_REPLY when nothing changed, and they check an alert ledger before pinging me twice about the same thing.

Telegram never waits

Slow work (vault pushes, builds, network calls) runs in the background. She answers first and reports the side effect later, so she never seems to go cold.

Ask before anything leaves the house

Reading and organizing are free. Sending email, posting or buying needs my yes. Judged fantasy football moves are the one standing exception. Gmail runs with a no-send guard.

The workspace is a git repo

Her identity, rules, scripts and contracts are versioned, so every change to how she behaves has a commit message. I also save a dated backup of the config before every change.

Build your own

Build your own in an evening.

You need a Mac that can stay on (an old laptop is perfect), a Telegram account, and a ChatGPT plan or an API key.

My recommendation

Don't type these yourself. Hand the whole thing to Claude Code.

Install Claude Code on the agent Mac, open a session, and paste the prompt below. It will run each step, read the errors, check openclaw --help when a flag has changed, and only stop when it needs you: approving macOS prompts, signing in to ChatGPT, talking to BotFather, and DMing your bot to pair. That's how I built and maintain Cora. The steps further down are what it will do, so you can follow along.

Set up OpenClaw on this Mac as an always-on personal agent I talk to on Telegram.
Follow John Wulff's Cora Field Guide (I'll paste the setup steps below) and:
- run every step yourself; stop only for things I must do (BotFather, ChatGPT
  sign-in, macOS permission prompts, pairing from my phone)
- keep every secret in a chmod 600 file under ~/.secrets, referenced from config
- bind the gateway to loopback, DMs allowlisted to me, groups mention-only
- back up ~/.openclaw/openclaw.json before every config change
- verify with openclaw doctor, openclaw security audit, and a real Telegram reply
- then interview me to write IDENTITY.md, SOUL.md, USER.md and a small HEARTBEAT.md,
  and commit the workspace to git
Ask before installing anything not in the guide.

Paste the steps below after the prompt, or just share this page's link with the session.

What the session will run

  1. Make the Mac an always-on server

    sudo pmset -a sleep 0 disksleep 0 autorestart 1

    Keep FileVault and the login password on. After a power cut, log in once so the agent's background service starts again. If you want to manage the Mac from another computer, turn on Remote Login under System Settings → General → Sharing and use SSH keys only.

  2. Install Homebrew, Node and OpenClaw

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    brew install node git gh
    npm install -g openclaw@latest --allow-scripts=openclaw
    openclaw --version

    Check npm --version first. On npm 11.15 or earlier, drop --allow-scripts=openclaw. Newer npm blocks OpenClaw's install scripts without it.

  3. Create the Telegram bot

    In Telegram, message @BotFather: /newbot, pick a name and a username ending in _bot, and copy the token. Then under /mybots → Bot Settings set Group Privacy: off (so it can read groups) and Allow Groups: off (so strangers can't add it). Flip Allow Groups on only while you add it to a group.

  4. Put secrets in files

    Copy the bot token to your clipboard, then:

    mkdir -p ~/.secrets && chmod 700 ~/.secrets
    pbpaste > ~/.secrets/telegram-bot-token && chmod 600 ~/.secrets/telegram-bot-token
    pbcopy < /dev/null

    Do the same for any API keys later (openai-api-key, anthropic-api-key, brave-api-key).

  5. Run onboarding with ChatGPT sign-in

    openclaw onboard --auth-choice openai --agent-name <your-agent-name>

    It walks you through the model, gateway, workspace and Telegram. When it asks for the bot token, paste it once. The next step moves it into the secrets file.

  6. Point the token at the file and lock things down

    openclaw config set secrets.providers.telegramtoken --provider-source file \
      --provider-path ~/.secrets/telegram-bot-token --provider-mode singleValue
    openclaw config set channels.telegram.botToken --ref-provider telegramtoken \
      --ref-source file --ref-id value
    openclaw config set gateway.bind loopback
    openclaw config set channels.telegram.groups '{"*":{"requireMention":true}}' --strict-json
  7. Install the service and pair yourself

    openclaw daemon install
    openclaw doctor --fix
    openclaw security audit

    DM your bot. It replies with a pairing code and your Telegram user ID, so write the ID down. Approve the code, then close the door so nobody else can ask:

    openclaw pairing list
    openclaw pairing approve <code>
    openclaw config set channels.telegram.allowFrom '["<your-telegram-user-id>"]' --strict-json
    openclaw config set channels.telegram.dmPolicy allowlist
  8. Give it a personality and version it

    The workspace files are the agent. IDENTITY.md is its name and vibe, SOUL.md its values, USER.md is about you, AGENTS.md holds the rules, and HEARTBEAT.md is the checklist it runs every 30 minutes.

    cd ~/.openclaw/workspace
    git init && git add -A && git commit -m "Birth"
    gh auth login
    gh repo create <your-agent-name>-workspace --private --source . --push
    open -e IDENTITY.md SOUL.md USER.md HEARTBEAT.md

    If this is the Mac's first commit, git will ask who you are. Set it once with git config --global user.name "Your Name" and git config --global user.email you@example.com.

    That private GitHub repo is your off-machine backup. Run git status before each push so nothing you didn't mean to share goes up. Your secrets live in ~/.secrets, outside the workspace, so they never get committed.

    Start HEARTBEAT.md small. Mine began as “check email, tell me if anything is urgent.”

  9. Add a fallback model chain

    Optional, and it needs pay-as-you-go API keys from OpenAI and Anthropic saved as files in step 4. Merge this into ~/.openclaw/openclaw.json. It defines the two metered providers, points them at your key files, and puts them behind your ChatGPT plan:

    "secrets": { "providers": {
      "oaikey":       { "source": "file", "path": "~/.secrets/openai-api-key",    "mode": "singleValue" },
      "anthropickey": { "source": "file", "path": "~/.secrets/anthropic-api-key", "mode": "singleValue" }
    } },
    "models": { "providers": {
      "openai-metered": {
        "baseUrl": "https://api.openai.com/v1", "api": "openai-responses",
        "apiKey": { "source": "file", "provider": "oaikey", "id": "value" },
        "models": [{ "id": "gpt-5.6-luna", "name": "GPT-5.6 luna (metered)", "input": ["text", "image"] }]
      },
      "anthropic-metered": {
        "baseUrl": "https://api.anthropic.com", "api": "anthropic-messages",
        "apiKey": { "source": "file", "provider": "anthropickey", "id": "value" },
        "models": [{ "id": "claude-haiku-4-5", "name": "Claude Haiku 4.5 (metered)", "input": ["text", "image"] }]
      }
    } },
    "agents": { "defaults": { "model": {
      "primary": "openai/gpt-6-sol",
      "fallbacks": ["openai-metered/gpt-5.6-luna", "anthropic-metered/claude-haiku-4-5"]
    } } }

    Use the full path (/Users/you/.secrets/…) if ~ doesn't resolve. Then run openclaw models status to confirm every link authenticates.

  10. Schedule your first job

    openclaw cron add --name "Morning brief" --cron "30 7 * * *" --tz America/Los_Angeles \
      --session isolated --announce --channel telegram --to <your-telegram-user-id> \
      --tools "web_search" \
      --message "Look up today's weather for <your city>. Send a 2-line brief. If it's an ordinary day, reply NO_REPLY."

    Your Telegram user ID is the one from the pairing reply. Use --model to put busy jobs on a cheaper model.

  11. Turn on dreaming and a nightly restart

    openclaw config set plugins.entries.memory-core.config.dreaming.enabled true
    cat > ~/Library/LaunchAgents/local.openclaw-nightly-restart.plist <<'EOF'
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0"><dict>
      <key>Label</key><string>local.openclaw-nightly-restart</string>
      <key>ProgramArguments</key><array>
        <string>/bin/launchctl</string><string>kickstart</string><string>-k</string>
        <string>gui/501/ai.openclaw.gateway</string>
      </array>
      <key>StartCalendarInterval</key><dict>
        <key>Hour</key><integer>3</integer><key>Minute</key><integer>40</integer>
      </dict>
    </dict></plist>
    EOF
    sed -i '' "s#gui/501/#gui/$(id -u)/#" ~/Library/LaunchAgents/local.openclaw-nightly-restart.plist
    launchctl load ~/Library/LaunchAgents/local.openclaw-nightly-restart.plist

    The sed line swaps in your own user ID, in case it isn't the usual 501.

  12. Then grow it one job at a time

    Add an MCP server (openclaw mcp add), give a group chat its own prompt, or wrap a data source in a small script that only prints what's new. Run openclaw cron list to see everything at a glance.

Scar tissue

Gotchas I paid for so you don't have to.

A /model pin disables fallbacks

A per-session model override turns off the fallback chain. When that model vanished from the catalog, Cora answered every message with an error while cron kept running. Avoid pins, or clear them from sessions.json.

launchd doesn't see your shell's env

A key exported in .zshrc works in the CLI and is missing in the gateway. Memory search silently went stale until I wired the key as a file SecretRef. Test through the running gateway.

Back up before every config edit

cp openclaw.json openclaw.json.bak-<what>-$(date +%Y%m%d). I have fifteen of these and have used several.

macOS privacy prompts block silently

Messages, Photos and Contacts need Full Disk Access for the process running the gateway. Grant it while you're at the keyboard, or a job will hang on a dialog no one sees.