PostHog loads after the page is idle through a same-origin /ingest proxy and follows the signed-in Convex user. Plausible uses the per-site script through a /pa proxy. Sentry keeps errors and traces only. Pages load only the @spoon/ui modules they import, and client code reads public env from publicEnv.ts so zod stays out of the browser. Both analytics integrations stay off until their env is set in Infisical prod.
Each job logs in as the read-only spoon-ci machine identity and exports the Infisical prod environment, so production values no longer live in Gitea. After pushing the images, CI asks the VPS to redeploy podman-spoon.service over the forced-command deploy key, with the key passed through env so the printed script never contains it. Installs survive a half-written runner cache, and one production deploy runs at a time.
Infisical prod carries a valid JWKS; the copy in DOTENV_PROD had escaped quotes, so the Convex env sync had been serving invalid JSON at /.well-known/jwks.json. The first sync from Infisical replaces it.
- Box init + chownInBox exec explicitly as root: under podman keep-id the
container's default user is the mapped uid, which cannot useradd/chown.
- Init renames an existing uid-1000 passwd entry (podman keep-id injects one
named after the HOST user) instead of adding a duplicate that would win the
getpwuid lookup and make whoami report the wrong name.
- Terminal connections tag their shell with a per-connection marker and reap
the process group on WebSocket close: closing the attach socket never
killed the exec'd shell, so every disconnect leaked an attached tmux client
(session stuck at the smallest zombie's size; enough dead clients starve
tmux rendering for live ones).
- Job image: nvm 0.39.5 at /usr/local/nvm (user-writable) with
/etc/profile.d/nvm.sh, matching what user dotfiles expect; docs for the box
user + ownership model.
- agent-job image: add neovim, tmux, less, unzip, wget, locales for the
interactive shell (tmux powers cross-reconnect session persistence)
- Wire NEXT_PUBLIC_SPOON_AGENT_WORKER_WS_URL as a build arg (Dockerfile +
compose.yml) since NEXT_PUBLIC vars are inlined at build time
- docs/agent-terminal.md: architecture, env, nginx WS exposure, dev testing,
security; note the build-time var in docs/compose.prod.yml
- Pin [email protected].0 + [email protected].9 in the job image (was @latest,
causing dev/prod drift)
- Worker now s the job image once per process so prod stops
running a stale Codex
- Surface Codex error/turn.failed events instead of swallowing them, so the
real failure reason is reported rather than 'no assistant response'
- Harden the Codex JSON parser to also handle the legacy msg-wrapped shape
- Fix the docker-in-docker workdir: bind-mount identical host:container path
and set SPOON_AGENT_HOST_WORKDIR (named volume can't be mounted by sibling
job containers)
- Add docs/compose.prod.yml as a documented reference deployment