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.usernameis 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 viasecretCrypto). - Dotfiles repo (optional) — a public git repo URL + optional ref + a
setup script path. On start the container clones it to
~/.dotfilesand runsbash ~/.dotfiles/<setup>(e.g. a bootstrap that symlinks configs, like the user's Panamainstall). - 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:
fetchUserEnvironment(jobId)— a worker-token Convex action (userDotfilesNode.getEnvironmentForJob) returns the owner's decrypted dotfiles + repo/setup config.materializeUserHome— ensures~/.bash_profile(so login shells source~/.bashrcin 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.