One repo that defines three machines: ghost (Mac Studio, the always-on compute machine), silence (MacBook Pro, the portable client), and the box (a Linux agent machine). Config is shared through Stow. Programs are installed directly — no Homebrew. Each machine keeps its own secrets and app state.
- ghost — always on. Owns all projects, the OpenCode service, Postgres, the SSH host, and the Zed/Cursor remote servers. Work happens here.
- silence — portable client. UIs run here; files, tools, and agents act on ghost over Tailscale + SSH. silence is set up; its ongoing job is to stay in sync with ghost.
- the box — Linux (Debian, x86_64) agent machine with Claude Code +
pstack, plus the OpenCode CLI. Same Stow tree and pinned tools as the Macs,
Linux builds. Runs no OpenCode service and no Postgres. Joins the tailnet
as
grokboxthrough its own tailscaled (no systemd there), sossh ghostand doctor's parity checks against ghost work once it is logged in. Linux-only behavior sits behinduname -schecks in the scripts or in the gitignored.localfiles.
home/— the Stow package (dotfiles), target$HOMEscripts/doctor.sh— read-only health check for the current machinedocs/tools.md— how programs are installed and updated (read this before touching any tool)docs/workflows.md— the daily operating manual
Every CLI tool lives at ~/.local/opt/<tool>/ with symlinks to its
executables in ~/.local/bin, which .zshenv puts first on PATH. Runtime
state (Postgres data, logs) lives in ~/.local/var/. Two exceptions: OpenCode
installs itself to ~/.opencode/bin, and desktop apps are .app bundles in
/Applications (see the GUI apps section of docs/tools.md).
To install or update any program — "install bun", "update nvim", anything — follow the playbook in docs/tools.md. It is the same procedure for every tool. This README and that file together are the source of truth: when you change a tool, update its entry in the inventory in the same commit.
cd ~/Developer/environment
stow --no-folding -n -t "$HOME" home # dry run — read it
stow --no-folding -t "$HOME" home # apply--no-folding links files but leaves parent directories real (~/.cursor
and the Cursor User directory must stay real; the apps write state into
them).
- Edit files under
home/in this repo. A GUI may rewrite a linked file (Zed settings, Cursor settings, OpenCode config): commit real setting changes,git restorethe noise. - Stowed files:
.zshenv(PATH),.zshrc,.ssh/config, Neovim config, Ghostty config, Zed settings, OpenCodeopencode.jsonc, Claude Code~/.claude/settings.json, Rex~/.config/rex/init.lua, and Cursor rules (~/.cursor/rules/*.mdc). CursorUser/settings.jsonis deliberately not stowed — the app rewrites it as state (seedocs/workflows.md). - Machine-specific values go in the gitignored
.localfiles, which the stowed configs source automatically:~/.zshrc.local(aliases),~/.zshenv.local(env vars),~/.ssh/config.local(SSH overrides). - On the other machine:
git pull— existing symlinks update in place, no re-stow needed. On a machine without the links yet, run Stow after pulling. - If the dry run reports a conflict, move that file out of
$HOMEand retry.
Pin a version, put the program in ~/.local/opt/<tool>, symlink its entry
points into ~/.local/bin, record the version in the tools inventory,
verify with doctor.sh, repeat on the other machines. Full procedure:
docs/tools.md.
The direct release tools (nvim, pnpm, bun, node, gh, cloudflared, claude) are scripted:
scripts/sync-tools.sh installs or updates them to the pinned versions on
whichever machine it runs, macOS or Linux (--check is read-only; doctor
uses it). stow
and postgres (source builds) and opencode (self-updating) stay manual by
design.
cd ~/Developer/environment && scripts/doctor.shA clean run means the Stow tree is settled and this machine's checks passed.
Run it after every change, on every machine. It detects its role: ghost by
hostname, the box by uname -s = Linux, otherwise silence.
.gitignore blocks the copies that would land inside home/:
- SSH private keys,
known_hosts,authorized_keys - the
.localoverride files - OpenCode
service.json,auth.json, and~/.local/share/opencodedata - Cursor
argv.json(per-machine crash-reporter id),cli-config.json, plugins, extensions, project caches, and other app state the apps rewrite
- No Homebrew. Every tool follows the direct-install pattern, or documents
its exception in
docs/tools.md. - Docs are part of the system. Any tool change updates the inventory in
docs/tools.mdin the same commit. - Secrets and app state never enter the repo. Machine-specific config goes
in the
.localfiles. - ghost, silence, and the box run the same tool versions. Doctor runs clean on all three.