Files

4.4 KiB

Personalized dev environment (dotfiles + persistent home)

Makes the workspace terminal feel like the user's own machine: a Fedora image preloaded with QoL CLI tooling, a persistent per-user home, and user dotfiles.

The model

  • Persistent per-user home. Each user gets a home directory on the worker host at ${SPOON_AGENT_WORKDIR}/homes/{username}, bind-mounted into every job/terminal container at /home/{username} (HOME). It survives across sessions, so dotfiles, installed tools, nvim plugins, and shell history persist. username is derived from the user's profile first name (sanitized).
  • Threads as folders. Each thread's checkout lives at ~/Code/{spoon}/{branch} inside that home, so every thread shows up as a folder in one home. The agent (codex --cd …) and the terminal both open there.
  • Neutral defaults (everyone). The Fedora job image (docker/agent-job.Dockerfile) ships zoxide, eza, bat, fzf, fd, ripgrep, gh, gum, neovim, tmux, oh-my-posh, etc., plus system-wide defaults that work even with an empty home: /etc/profile.d/spoon.sh (tool init + aliases), /etc/tmux.conf (login-shell panes), /etc/spoon/omp.json (prompt theme).
  • User dotfiles (per-user). Configured in Settings → Dotfiles, applied on top of the neutral defaults.

Settings → Dotfiles

A mini file-browser workspace rooted at home/{firstName}:

  • Editable overlay tree — drag in files/folders (or use Upload folder/files), edit them in the Monaco editor, add/delete. Files are placed relative to $HOME (.bashrc~/.bashrc, .config/nvim/…~/.config/nvim/…). Stored encrypted at rest (userDotfiles, AES-256-GCM via secretCrypto).
  • Dotfiles repo (optional) — a public git repo URL + optional ref + a setup script path. On start the container clones it to ~/.dotfiles and runs bash ~/.dotfiles/<setup> (e.g. a bootstrap that symlinks configs, like the user's Panama install).
  • Precedence (hybrid): repo clone + setup runs first; then the editable overlay is written on top — overlay wins.

Secrets: dotfiles are encrypted, but real API keys/tokens belong in a Spoon's Secrets feature (injected as env vars), not in dotfiles. The UI nudges this.

Materialization (worker)

apps/agent-worker/src/user-environment.ts:

  1. fetchUserEnvironment(jobId) — a worker-token Convex action (userDotfilesNode.getEnvironmentForJob) returns the owner's decrypted dotfiles + repo/setup config.
  2. materializeUserHome — ensures ~/.bash_profile (so login shells source ~/.bashrc in a mounted home with no /etc/skel); clones the repo + runs the setup command inside the job image (so the user's tools/paths apply), only when the config hash changes (~/.spoon/env-hash); writes the overlay files.

Configuration

Variable Default Notes
SPOON_AGENT_WORKDIR .local/agent-work (dev) / /var/lib/spoon-agent/work (prod) Per-user homes live under homes/{username}; reuses the existing host-path translation.
SPOON_ENCRYPTION_KEY / INSTANCE_SECRET Already required; encrypts dotfiles like other secrets.

No new required env. The home is a host directory under the existing workdir, so the prod bind-mount + SPOON_AGENT_HOST_WORKDIR translation already covers it.

Notes / limits (Phase 1)

  • Repo auth: public repos only. Private/self-hosted (e.g. Gitea) dotfiles repos are a follow-up (store a token/deploy key).
  • Binary files: the overlay is text-first.
  • Cleanup: ~/Code/{spoon}/{branch} checkouts persist (threads as folders); a per-thread "delete checkout" action is a follow-up.
  • Concurrency: jobs share one home; fine at the default SPOON_AGENT_MAX_CONCURRENT_JOBS=1.

Phase 2 north star

A single long-running per-user container that every thread execs into (agent via docker exec, not docker run --rm). The per-user home + ~/Code/{spoon}/ {branch} layout built here is its foundation.