claude-computer
GitHub
New Building solo? See my dev workflow

A GitHub template · MIT

Mac + Claude.
Dev Box Setup

A GitHub template that sets up a Mac for a solo developer. It assumes you have a Claude Max subscription and like terminal-first development in Claude Code — you step in for the key decisions, judgement and taste. Mac + Claude is the best combination IMO. The point is to get things done FAST, by cutting the time you spend on cruft.

  • you authenticate
  • you set the mode
  • you decide
Terminal.app
# one command
$ /bin/bash -c "$(curl -fsSL https://claude-computer.com/install.sh)"

# or the four commands by hand — see Quick start

then paste docs/FIRST-PROMPT.md

01

The idea

The usual order

  1. Set up the machine
  2. Install your tools
  3. Maybe add an AI assistant to help you code

This order

  1. Xcode command line tools, Homebrew, gh, Claude Code
  2. Hand it the machine
  3. Everything else is a conversation

It's part stack (Claude + Tailscale + GitHub + sops + Cloudflare + Obsidian + Homebrew), part organization (an opinionated take on folders and repos), part process (slash commands, hooks, scheduled jobs) — all designed to take advantage of the Mac + Claude combination.

I built this after spending a year arriving at a stack, pattern and process that has let me up my output exponentially. All the things a solo dev ends up losing an afternoon to — install this, configure that, why is this port open, set up the new box in the cupboard, rotate this key — go to Claude instead. Install the operator first and the rest follows. A coding assistant sees one repo. An operator sees the system the repos live in: which machine this is, what's installed, where secrets come from, how things sync, what runs on a schedule. Give it that context once, written down in files it maintains, and every later session — including every coding session — starts from a true picture instead of a guess.

It is opinionated and reflects my learnings, taste and judgement. Change what doesn't fit.

The three jobs you keep

Authenticate
Every login, every password typed, every OAuth consent screen, every "allow" on a macOS permission prompt. Claude never handles a credential.
Set the mode
Claude Code runs in auto mode: a classifier model reviews each action instead of you, and your ask and deny rules still hold. Never bypass permissions.
Decide
Claude proposes; you choose. Anything destructive, anything touching credentials, anything that costs money — it asks first, every time.
02

How it works

Three parts. The stack is what gets installed. The organization is where things live — folders, one repo per project, a brain per folder, a vault for the notes, a file per machine. The process is what runs without you thinking about it — slash commands, hooks, scheduled jobs.

The stack

Claude Code is the operator. GitHub holds every repo, including the fleet brain that syncs between machines. Tailscale is how they reach each other (read about my fleet). One sops-encrypted file in your own repo holds every machine secret, opened by an age key on each machine — no secrets service, no unlock step. Cloudflare R2 is cold storage, no egress fees. Obsidian is the second brain — plain markdown in a git repo, so the notes stay as portable as the code. Homebrew installs the rest, from Brewfiles layered by role. Nothing exotic — each piece earns its place by being drivable from a terminal.

The map, and map-check

The home directory gets a handful of top-level folders — clients, projects, products, personal, vault, archive, library. They are containers, not repos; repos live one level down — one per project, and one per client. Then one file per machine records what is installed, what is listening, what is scheduled, where the keys live (never the keys). bin/map-check diffs those sections against reality — brew leaves, the LaunchAgents directories, lsof, the VS Code extension list — and then runs security-check. Drift is a bug: fix the machine or fix the map, in the same session.

Diagram of the home directory: claude-computer, clients, projects, products, personal and vault sync with git to GitHub; archive, library/camera and library/books sync one-way to Cloudflare R2; resources syncs two-way, encrypted. Manager machines on a Tailscale tailnet reach headless boxes over SSH. a sops-encrypted secrets file feeds every script; a Telegram bot receives from every machine.
Where everything lives, and the one way each folder leaves the machine. Scroll sideways to read it all.

The session loop

~/claude-computer is your private repo, cloned on every machine that runs Claude Code. The SessionStart hook pulls with --rebase --autostash and reports whether the secrets store opens. The Stop hook commits any docs/ change as [host] update … and pushes, at the end of every turn — because sessions get killed far more often than they get exited. Each machine writes only its own file, so conflicts are rare by construction and DECISIONS.md is append-only, so it always merges.

The session loop: the SessionStart hook pulls the repo and checks the secrets status; Claude orients by reading CLAUDE.md, the machine file and FLEET.md; work happens; changes are recorded in the machine file, DECISIONS.md and TASKS.md; the Stop hook commits docs as [host] and pushes.
Hooks do the syncing, not discipline.

Trust

Auto mode is only sane with guard rails. git push, rm, every rclone command that writes, sudo, the storage scripts and every write to the secrets store sit on the ask list, which prompts even in auto mode; force-push, decrypting the store and reading the age key or browser profiles sit on the deny list, which blocks in every mode. Your own rules live in an overlay that settings-build merges with the template's, and it refuses to drop a deny rule or a secrets ask. One SSH key per machine, generated on it and never copied. Every machine secret is in one sops-encrypted file that the scripts decrypt a value at a time and Claude never opens — no .env with keys, no rclone.conf with credentials on disk, no secret on a command line. Time Machine is the net: auto mode on a machine you care about needs a whole-machine rollback.

CLI first, MCP last

An MCP server loads its full tool schemas into every session's context, whether you use it or not. So each service gets a thin wrapper in bin/ instead — tavily, exa, firecrawl, jina, browse, gcal, gmail, gdrive, gtasks — that decrypts its key from the secrets file and prints markdown. Standing context cost: one paragraph in CLAUDE.md. MCP is reserved for things that are stateful or OAuth-bound in a way a wrapper can't handle.

A brain per folder

Any folder that needs automation gets its own CLAUDE.md — a brain — and a TASKS.md. You start Claude where the work is; that brain says what to do here and points at the fleet repo for what the system looks like. archive/ archives and retrieves. library/camera ingests cards and finds photos by metadata. Folders that just hold files have no brain.

The Second Brain, filed by Claude

Obsidian is the second brain — plain markdown in a git repo — but Claude does the filing. PARA was designed for a human doing the filing. With Claude doing it, the constraint flips: capture should cost nothing, and filing, linking and retrieval are Claude's job. So the vault is built for cheap capture and for Claude finding and connecting things, not for where a person would browse. Everything lands in inbox/ — dictation, meetings, web clips, a message to your Telegram bot, an email you label. Two layers sit under it: notes/ and daily/ are yours, in your words; memory/ is Claude's graph of people, meetings, decisions and commitments, every fact citing its source. At 04:30 the night shift files the day into it, and lands nothing unless vault-lint passes. Tasks stay in the TASKS.md of the brain that owns them; the vault gets read-only copies.

How the vault works

If it can be a script, it is one

Slash commands are for steps that need judgment, and they orchestrate scripts rather than reimplement them. Hooks are for what must happen every time. Scheduled launchd jobs are for what must happen without a session. Everything deterministic lives in bin/, answers --help, and returns exit codes instead of prose. A task longer than five steps gets a live dashboard page first — tasks, questions with a proposed default, deliverables, blockers — so a question for you never stops the work that doesn't depend on it.

03

Features

What you get, by the command that does it. Every one is a script in bin/ or a slash command that calls one, and every script answers --help. The full list, with what triggers each, is on What's inside.

Run the machines

The operator and the fleet brain

One private repo, cloned on every machine at ~/claude-computer. Claude reads this machine's file before it acts and updates it after every change, so each session starts from a true picture instead of a guess.

docs/machines/<host>.md
the map: what is installed, listening and scheduled, and where the keys live — never the keys
map-check
diffs the map against Homebrew, launchd, open ports, VS Code and the settings file; daily at 09:00, a Telegram line on drift
security-check
FileVault, firewall, sshd, Tailscale, private repos and browser profiles
DECISIONS.md
append-only, every line tagged [host] [date], so it always merges

Secrets: sops and age

Every machine secret is in one file, secrets/secrets.yaml, encrypted with sops to an age key on each machine and a paper backup key. Names in clear, values not. No secrets service, no unlock step. Scripts decrypt one value at a time; Claude never decrypts anything.

secrets set name.field
a hidden prompt — you type every value, never as an argument
secrets exec --only NAME -- cmd
runs a project with only the keys it needs
secrets admit · revoke
re-encrypt for a new machine, or drop one and list what to rotate
secrets status · check
names and metadata only; nothing is decrypted

Permissions and safety

Claude Code runs in auto mode: a classifier reviews each action, and rules sit on top. Ask prompts even in auto mode; deny blocks in every mode. Pattern matches, not a sandbox — Time Machine is the net.

ask
git push, rm, sudo, every rclone command that writes, the storage scripts, every write to the secrets store
deny
force-push, decrypting the store, the age key, .env files, SSH keys, browser profiles
settings-build
builds ~/.claude/settings.json from the template's rules plus your own settings.overlay.json; it won't remove a deny rule or a secrets ask, so your rules survive every release
--check · --adopt
a "don't ask again" or /config change shows up as drift; --adopt keeps it in the overlay

Staying current

An instance made with "Use this template" shares no history with it, so git merge refuses. template-pull applies the template's own diff between two releases instead, one file at a time: your changes merge three-way, and a file that can't be merged stays yours and is listed, while the rest still applies.

template-pull
lists the releases, and the one you are on (.template-version)
template-pull <tag> --dry-run
what would change, merge and be left for you, tried in a throwaway worktree
template-pull <tag>
stages the release, commits nothing, and ends with the changelog for every release in between
/upstream
a lesson from your instance, rewritten generically, as a pull request against the template

Reach the outside world

Research, without MCP

Thin wrappers instead of MCP servers: each decrypts its own key and prints markdown, and costs no context until it is used. Agent Reach adds YouTube, GitHub and RSS.

tavily "q" · exa "q"
web search, and semantic search
firecrawl <url>
a page as clean markdown
jina <url> · --search
read or search through Jina Reader; works without a key, at a low rate limit
browse <url>
Playwright's Chromium for pages that need JavaScript or a login you made yourself; --screenshot, --pdf, --profile

Google, from the shell

One sign-in, which you do; its token sits in the secrets store and every Google wrapper shares it.

gcal today|day|at|search|add
every calendar; at finds the meeting a recording overlapped
gmail search|get|draft|send|label
attachments in and out; Claude asks before every send
gdrive search|download|upload|share
Docs, Sheets and Slides come down as docx, xlsx and pptx
gtasks list|handoff|done
Google Tasks as the list you jot into from anywhere; /review hands each task to a brain
analytics setup|sync|report
GA4 and Tag Manager from a project's analytics.yaml; live only when you say publish

Remember

The vault and its memory graph

Obsidian over a git repo, in two layers. notes/ and daily/ are yours. memory/ is Claude's graph of people, orgs, meetings, decisions, commitments and projects, built only from evidence in sources/ — every fact a dated line that cites it.

/transcripts
a recording becomes a meeting note: attendees from the calendar, decisions, commitments, action items routed to the brain that owns them
/inbox
your thinking into notes/, material from elsewhere into sources/, people and decisions pulled out of both
vault-lint
checks notes against the vault's schema — properties, links, cited log lines, duplicate people; an unknown property is a warning with a "did you mean"
commitments
what is owed, yours first; brief puts what's overdue and who you met yesterday in the daily note, and closing one files your word as the evidence
project-import
snapshots a GitHub repo into the vault, so a project's hub outlives its checkout

The night shift

vault-nightly runs at 04:30 and files the day's transcripts and captures into the graph with claude -p, nobody watching. It is fenced, so a bad night costs nothing.

a worktree
Claude edits a git worktree of the vault, never the vault
dontAsk
a short list of allowed tools; anything else is refused, not left waiting for an answer
a gate
it merges only if vault-lint is clean, nothing outside inbox/ was deleted and nothing is left uncommitted
## Overnight
what it did, at the top of the next daily note; a failed run changes nothing and tells you

Capture and voice

Capture is one move; filing is Claude's job. Speech becomes text on the Mac, through MacParakeet — nothing leaves the machine to be transcribed.

capture
every 5 minutes: messages, photos and voice notes you send your Telegram bot, and email you label vault, into the inbox
transcripts-sync
watches MacParakeet's database and exports each finished meeting
macparakeet-cli transcribe
a file, a URL or a podcast, straight into the vault
feeds-sync
at 06:00, RSS and newsletters as one reading digest

Build and keep

Live dashboards

A task of more than five steps, or over half an hour, gets a live page first: .dashboard/index.html in the project. One self-contained file — it opens with a double-click, works offline and reloads every 10 seconds.

dashboard init · open
the page and its tasks, its layout designed once
dashboard task|deliverable|blocker
updated after every step
dashboard question --default
a question for you with Claude's proposed answer; work that doesn't depend on it carries on
dashboard snapshot
a copy for attaching, labelled as one

Project scaffolding

A new project is one command, with its brain, its task list, the secret scan and a private GitHub repo from the first commit. How each is built is the dev workflow.

new-app web|mobile|cli|desktop
the stack's own scaffolder, then a CLAUDE.md, TASKS.md, a justfile and docs/
new-client <name>
~/clients/<name> as a private repo of its own, new or adopted, its brief versioned
docs-build
checks a project's docs/ — front matter, links, anchors — and builds llms.txt and one offline reading page
https-check · /graduate
http to https on every live domain; a project in production moves to ~/products

Storage in R2

Cloudflare R2 is cold storage, with no egress fees. The credentials come from the secrets store for the length of one command; there is no rclone.conf with keys on disk.

archive-push · /archive
moves a folder to R2, verifies it, rebuilds the index; deletes the local copy only when you say
archive-pull --search · /retrieve
finds it in the index and restores it, never overwriting
resources-sync
hourly, an encrypted two-way sync of family documents
library-push · camera-ingest
cold copies of the book and comic libraries and of every memory card, verified, never deleting the source
04

What this isn't

Read this before you run it on a machine you care about.

  • It is my opinionated setup, not a neutral framework. sops, Tailscale, Cloudflare R2, Obsidian, Ghostty, herdr, Homebrew. Swap what doesn't fit — but you are swapping, not configuring.
  • Permission rules are pattern matches, not a sandbox. The ask and deny lists are string and glob rules on commands; auto mode adds a classifier on top. Both reduce prompts and mistakes. Neither is a security boundary, and nothing here contains a program that has already decided to do something else.
  • macOS first; Linux is thin. Headless Linux boxes are managed over SSH, but /setup, the Brewfiles and the macOS defaults assume a Mac.
  • It expects you to keep doing the three jobs. If you turn on bypass permissions, skip Time Machine, or let the map drift, none of the rest of this helps you.
  • It is young. I have run this approach on my own machines for about a year; the template is that approach written down. /setup has been dry-run and read end to end. If you try it, I would love to see your /setup report.

The Trust section of the README is the part to read closely: README · Trust

05

Quick start

Before you start

  • A Mac on a recent macOS.
  • A GitHub account.
  • A Claude Max subscription. All-day sessions in auto mode are the whole point, and they outrun anything smaller.
  1. Install the operator

    One command in stock Terminal.app. It checks what you already have — Xcode command line tools, Homebrew, gh, Claude Code — installs whatever is missing, and hands over.

    /bin/bash -c "$(curl -fsSL https://claude-computer.com/install.sh)"

    read it first — curl -fsSL https://claude-computer.com/install.sh | less
    see what it would do — add -- --dry-run

    It asks one question: what your private repo should be called. Take the default and you get <you>/claude-computer cloned to ~/claude-computer — and that is one repo for every machine you own, so on a second machine you run the same command and it clones that repo instead of creating another. Want a different name? I called mine sys-admin: -- --name sys-admin --dir ~/claude-computer, the same on every machine (why, and what happens if you get it wrong).

  2. Hand over

    The installer has already put this prompt on your clipboard. Log in to Claude Code and check the status line reads ⏵⏵ auto mode on (Shift+Tab cycles modes). Then paste the first prompt and do what it asks: log in, grant, decide.

    You are the operator for this computer, my network and the devices on it. Your
    brain is this repo: read CLAUDE.md now and follow it.
    
    Start with this computer. This is a new machine, so run /setup:
    
    1. Identify this machine (hostname, hardware, macOS version). Tell me the
       hostname you will use as its name in the fleet and let me confirm it or
       rename the machine first. Then ask me its role (client, build, server) and a
       one-line purpose. Write docs/machines/<host>.md from
       docs/machines/_example.md, add it to docs/FLEET.md, and append the decision
       to docs/DECISIONS.md.
    2. Create an SSH key for this machine only, add it to GitHub with gh, and push
       this repo.
    3. Install Tailscale and walk me through logging in. Record the
       Tailscale name in the map.
    4. Install age and sops and run bin/secrets init (--first on the fleet's
       first machine, then walk me through the paper backup key). Confirm
       bin/secrets status.
    5. Link claude-global/ into ~/.claude so the hooks and commands are live.
    6. Install the Brewfile layers for this role, link dotfiles, run
       macos-defaults.sh. Tell me before each step that needs a password or a
       macOS permission prompt.
    7. Run bin/map-check and bin/security-check, fix what fails, and update the map.
    
    Rules for the whole session: ask before anything destructive, anything that
    touches credentials, and anything that costs money. I will do every login and
    every step that handles a password myself. Record every decision we make in
    docs/DECISIONS.md. When something is deferred, put it in TASKS.md under Later.
    At the end, commit and push docs/ and tell me what is left.
Or do it by hand

The same thing, typed yourself: the installer runs exactly these, checking each first.

xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install gh && gh auth login
brew install --cask claude-code

After the Homebrew installer finishes, run the shellenv lines it prints — nothing else works until brew is on your PATH.

Your instance is a private repo made from the template, cloned into your home directory.

cd ~ && gh repo create claude-computer --template narendranag/claude-computer --private --clone
cd ~/claude-computer && claude

A second machine is much shorter: the same one-line installer, with the same repo name — it finds the repo already there and clones it instead of creating a second one — and then /setup, which reads the repo, finds everything already decided, and asks only what is specific to this machine. By hand it is gh repo clone <you>/claude-computer ~/claude-computer in place of creating from the template.

06

Help wanted

This is the point where it stops being one machine's setup. The most useful thing you can send me is what happened when you ran /setup on yours.

  • Setup reports What broke, what asked for a password it shouldn't have, what map-check got wrong on your hardware and macOS version.
  • Other cameras The camera brain reads my RAW workflow. Other bodies, other card layouts, other sidecar conventions.
  • Linux Headless boxes are managed over SSH today. A real Linux /setup — packages, dotfiles, the security baseline — is the biggest open gap.
  • Equivalents Another secret backend behind the same script interface, Backblaze instead of R2, WireGuard instead of Tailscale, iTerm2 instead of Ghostty — as clean swaps, not forks.
  • Another agent Fork it for Codex, Gemini CLI or a local model. The brains are markdown and the work is in bin/, so most of it ports. Tell me and I'll link it here.
07

Who's behind this

I'm Narendra. Twenty-eight years at the intersection of journalism, digital and streaming, and I now run Marain, an operator's practice — most advisors leave a deck, I leave a system that runs. I write about media, attention and the industries that shape how we spend it at narendranag.com. For the last year I've spent 90% of my time in Claude Code and the terminal, with a laptop, a Mac Studio running Qwen, a Mac Mini, a Hetzner VPS (managed entirely by Claude) and a Pi — all set up and run by Claude. This template is that setup, written down.