Files
headquarter/openspec/designs/tool-container-home-directory.md
Developer 5fc8e035e6 docs: tool container home directory design, plan, and test plan
- Add design doc / ADR for configurable /home/user home directory
   - Add implementation plan with phased rollout
   - Add test plan / QA checklist
   - Update OpenSpec task for home-path-expansion
2026-06-14 10:23:59 +00:00

5.8 KiB

Design / ADR: Tool Container Home Directory

Status

Proposed

Context

Tool containers currently mix home-directory conventions:

  • Legacy dockerfile and compose tool types mount the repository at /workspace and run as root.
  • Manifest-based tool types may run as a non-root user (user) and already support ~ / $HOME expansion to /home/{user.name} via the home-path-expansion work.
  • tool-images/base.dockerfile already creates a user with WORKDIR /home/user, but the platform does not guarantee this for all tool types.

Users expect a predictable, writable home directory inside every tool container and want the repository/workspace to live under it, preserving the repository's root directory name so paths are meaningful (e.g., /home/user/my-app, not /home/user/workspace or a generic /workspace).

Goals

  1. Provide a configurable workspace/home directory that defaults to /home/user for all tool containers.
  2. Mount the repository/workspace under that directory using the actual directory name ({repo_name} or {workspace_name}).
  3. Make the setting part of the ToolType / manifest definition so tool authors control it.
  4. Keep manifest.user.name as the runtime user; the new field controls the directory path.
  5. Preserve /workspace as a compatibility symlink to avoid breaking existing scripts, bookmarks, and settings.
  6. Ensure config-profile and git mounts staged by the API remain writable by the non-root container user.

Non-Goals

  • Changing the container runtime user model (still driven by manifest.user).
  • Moving non-tool services (API, web, Postgres, Redis) under /home/user.
  • Forcing every existing manifest to migrate; explicit repo mount targets remain respected.
  • Adding a frontend UI for the new field in this iteration (backend field + manifest schema change only).

Decision

Configuration surface

Add a home_directory field to the ToolType model and the manifest schema. It defaults to /home/user and can be overridden per tool type.

  • ToolType level: ToolType.home_directory: str = "/home/user" (non-nullable, default).
  • Manifest level: manifest.home_directory: str (optional; if absent, fall back to ToolType.home_directory).

The runtime home directory for a container is resolved in this precedence order:

  1. Manifest home_directory if present.
  2. ToolType home_directory (database column, default /home/user).
  3. Legacy fallback: /root for compose/dockerfile tools without the new field.

What home_directory controls

  1. Repository/workspace mount target when no explicit repo mount is defined:
    • Repositories: {home_directory}/{repo_name}
    • Workspaces: {home_directory}/{workspace_name} (fall back to {repo_name})
  2. Dockerfile HOME environment variable for manifest tools: ENV HOME={home_directory}.
  3. Dockerfile WORKDIR for manifest tools (when no explicit runtime.working_dir overrides it).
  4. Base for ~ / $HOME expansion in config-profile mounts and git-mount mappings.

Precedence: explicit manifest mount wins

If a manifest already contains a mount with source_type: repo and an explicit target, that target is used unchanged. home_directory is only used to synthesize the default repo/workspace mount when none is explicitly specified.

Migration for legacy tools

Use an Alembic data migration:

  1. Add home_directory column to tool_types.
  2. Set existing rows to /home/user.
  3. Rewrite stored compose_template and dockerfile_template strings to replace /workspace with {home_directory}/{{WORKSPACE_NAME}} (or equivalent template variable) where the mount target is the workspace.

The migration is reversible via downgrade: restore the original /workspace strings and drop the column.

/workspace compatibility

Keep /workspace as a symlink inside the container pointing to the resolved repo/workspace target (e.g., /home/user/my-app). This protects:

  • Existing user startup commands and scripts.
  • Bookmarks and IDE settings that reference /workspace.
  • Legacy templates that were migrated.

The symlink is created by the generated Dockerfile (or startup entrypoint) because the actual repo is mounted at runtime.

Permission model

Use an entrypoint permission fixer at container startup:

  • Run as root (or via sudo) before switching to the runtime user.
  • Chown mounted paths under {home_directory} to the container user.
  • Avoid chown -R on large repo subtrees; chown the top-level directories and rely on the runtime user owning newly created files.
  • This handles config-profile and git mounts that arrive at runtime as bind mounts from the host, which may otherwise be owned by root because the API stages them.

Consequences

Positive

  • Predictable, user-writable home directory across all tool containers.
  • Repository mount paths are meaningful (/home/user/my-app).
  • Backward-compatible symlink keeps existing user data and scripts working.
  • Tool authors can opt into other home directories without changing the runtime user.

Negative / Risks

  • Requires an Alembic data migration that rewrites stored templates; downgrade must be carefully tested.
  • Entrypoint permission fixer adds startup complexity and requires root/sudo inside the container.
  • Creating /workspace symlink at build time vs. runtime needs care because the target may not exist until the repo is mounted.
  • Legacy tool-images/base.dockerfile runs as USER user; it will need an entrypoint that can elevate permissions or the base image must be adjusted.
  • openspec/designs/home-path-expansion.md
  • openspec/specs/home-path-expansion.md
  • openspec/tasks/home-path-expansion.md
  • docs/superpowers/plans/tool-container-home-directory.md
  • docs/superpowers/specs/tool-container-home-directory-test-plan.md