Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca44da71e1 | |||
| 8e605e87d4 | |||
| 7a8de0463f | |||
| adaf7ba2ff | |||
| d426e92745 | |||
| b9c08c3dbb | |||
| 45d7e02faf | |||
| 4de0bc9993 | |||
| b648d83928 | |||
| f2f8a70dae | |||
| c34cf3641b | |||
| 3a7ec45f4b | |||
| e1029bbf27 | |||
| 8c919074dd | |||
| bca403c540 | |||
| c182ada0dd | |||
| b9657415c4 | |||
| b37740bcce | |||
| 3982e9f18c | |||
| 4d0c270196 | |||
| aed5ff106b | |||
| 425d53cb57 | |||
| 60208b2203 | |||
| d65f8cc077 | |||
| 4560702550 | |||
| c851b4cc8d | |||
| 9bb93025f0 |
+9
-3
@@ -1,7 +1,13 @@
|
|||||||
# ── Shared machine setup ─────────────────────────────────────────────
|
# ── Shared machine setup ─────────────────────────────────────────────
|
||||||
# Your corporate signum / username (REQUIRED)
|
# SIGNUM isolates your container name and named volumes from other users.
|
||||||
# This isolates your container, config, and data from other users.
|
#
|
||||||
SIGNUM=your-signum-here
|
# Own-account mode (each user has their own OS login):
|
||||||
|
# Leave SIGNUM commented out — it defaults to your OS username ($USER).
|
||||||
|
# SIGNUM=
|
||||||
|
#
|
||||||
|
# Shared-account mode (everyone logs in as the same OS user):
|
||||||
|
# Uncomment and set to your unique identifier.
|
||||||
|
# SIGNUM=your-signum-here
|
||||||
|
|
||||||
# ── Provider ─────────────────────────────────────────────────────────
|
# ── Provider ─────────────────────────────────────────────────────────
|
||||||
OPENCODE_PROVIDER=amazon-bedrock
|
OPENCODE_PROVIDER=amazon-bedrock
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
*~
|
*~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Docker buildx state (created by 'docker compose build')
|
||||||
|
.docker/
|
||||||
|
|
||||||
# Personal cloud-init overrides (not shared)
|
# Personal cloud-init overrides (not shared)
|
||||||
deploy/my-cloud-init.yml
|
deploy/my-cloud-init.yml
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ Docker image packaging [opencode](https://opencode.ai) into a production-ready d
|
|||||||
|
|
||||||
## Versioning scheme
|
## Versioning scheme
|
||||||
|
|
||||||
Tags follow `v{opencode_version}[letter]` — e.g. `v1.14.20` for the first build on a new opencode release, and `v1.14.20a`, `v1.14.20b`, … for subsequent rebuilds on the same opencode version.
|
Tags follow `v{opencode_version}[letter]` — e.g. `v1.14.20` for the first build on a new opencode release, and `v1.14.20b`, `v1.14.20c`, … for subsequent rebuilds on the same opencode version.
|
||||||
|
|
||||||
- The number tracks the opencode npm version (see `OPENCODE_VERSION` ARG in `Dockerfile`).
|
- The number tracks the opencode npm version (see `OPENCODE_VERSION` ARG in `Dockerfile`).
|
||||||
- **No letter suffix** on the first build of a new opencode version — the bare `v{opencode_version}` tag is the canonical release.
|
- **No letter suffix** on the first build of a new opencode version — the bare `v{opencode_version}` tag is the canonical release.
|
||||||
- **Letter suffix** (`a`, `b`, `c`, …) increments for container-level rebuilds — tooling changes, CVE fixes, doc-driven rebuilds, entrypoint bugfixes — that don't change the underlying opencode version.
|
- **Letter suffix is the build ordinal**, starting at `b` for the second build. The letter `a` is **never used** — think of the suffix as counting rebuilds: `b = 2nd, c = 3rd, d = 4th, …`. For opencode version `1.14.20`: first build `v1.14.20`, second `v1.14.20b`, third `v1.14.20c`, and so on.
|
||||||
|
- A letter suffix is only used for container-level rebuilds — tooling changes, CVE fixes, doc-driven rebuilds, entrypoint bugfixes — that don't change the underlying opencode version.
|
||||||
|
|
||||||
CI produces four Docker Hub tags per release: `vX.Y.Z[n]`, `latest`, `vX.Y.Z[n]-omos`, `latest-omos`.
|
CI produces four Docker Hub tags per release: `vX.Y.Z[n]`, `latest`, `vX.Y.Z[n]-omos`, `latest-omos`.
|
||||||
|
|
||||||
|
|||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to the opencode-devbox container image.
|
||||||
|
|
||||||
|
Tags follow `v{opencode_version}[letter]` — bare tag for the first build on a new opencode release, letter suffix (`b`, `c`, …) for container-level rebuilds on the same version. See [AGENTS.md](AGENTS.md#versioning-scheme) for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.14.28b — 2026-04-27
|
||||||
|
|
||||||
|
- **Feature:** Add MemPalace local-first AI memory system to base image. Provides 29 MCP tools for semantic search over conversation history, knowledge graph queries, and agent diaries. Palace data persists via optional `devbox-palace` named volume, ChromaDB embedding model cache via `devbox-chroma-cache`. No API keys required.
|
||||||
|
- **Feature:** Auto-register mempalace MCP server in generated opencode.json (when mempalace is installed and config is auto-generated from OPENCODE_PROVIDER).
|
||||||
|
- **Feature:** Add official Gitea MCP server (`gitea-mcp`) to base image. Provides 50+ MCP tools for Gitea API (repos, issues, PRs, releases, Actions). Disabled by default — requires `GITEA_ACCESS_TOKEN` and `GITEA_HOST` env vars.
|
||||||
|
|
||||||
|
## v1.14.28 — 2026-04-26
|
||||||
|
|
||||||
|
Bump opencode to 1.14.28.
|
||||||
|
|
||||||
|
## v1.14.25 — 2026-04-25
|
||||||
|
|
||||||
|
Bump opencode to 1.14.25. Also includes container-level changes since v1.14.22b:
|
||||||
|
- Add `python3-pip` and `python3-venv` to base image (fixes Mason LSP installs).
|
||||||
|
- Add `devbox-nvim-data` named volume for neovim plugin/Mason persistence.
|
||||||
|
- Add `devbox-zoxide` named volume for zoxide directory history persistence.
|
||||||
|
- Bake devbox-shell bridge line into `/etc/skel-devbox/.bash_aliases`.
|
||||||
|
- Add CHANGELOG.md with full release history.
|
||||||
|
|
||||||
|
## v1.14.22b — 2026-04-23
|
||||||
|
|
||||||
|
**Fix Mason LSP installs, persist nvim data, devbox-shell bridge.**
|
||||||
|
|
||||||
|
- **Fix:** Add `python3-pip` and `python3-venv` to base image. Mason creates a Python venv per LSP package and pip-installs into it; Debian trixie ships python3 without ensurepip, so venv creation failed and every Mason Python package (ruff, ansible-lint) errored on every nvim start.
|
||||||
|
- **Feature:** Add `devbox-nvim-data` named volume at `~/.local/share/nvim` — Lazy plugin cache and Mason LSP installs now persist across `--force-recreate`.
|
||||||
|
- **Feature:** Add `devbox-zoxide` named volume at `~/.local/share/zoxide` — zoxide directory history persists across recreates.
|
||||||
|
- **Feature:** Bake the devbox-shell bridge line into `/etc/skel-devbox/.bash_aliases` — hosts using the `~/.config/devbox-shell/` directory-mount pattern get automatic sourcing without manual setup after recreate.
|
||||||
|
|
||||||
|
## v1.14.22 — 2026-04-23
|
||||||
|
|
||||||
|
Bump opencode to 1.14.22.
|
||||||
|
|
||||||
|
## v1.14.21 — 2026-04-23
|
||||||
|
|
||||||
|
**Opencode 1.14.21 + zoxide persistence + multi-user fixes.**
|
||||||
|
|
||||||
|
- Bump opencode to 1.14.21.
|
||||||
|
- Fix single-file bind-mount caveat: document the kernel-level inode issue (affects all platforms, not just Docker Desktop).
|
||||||
|
- Pin project name in default `docker-compose.yml` — directory renames no longer orphan named volumes.
|
||||||
|
- Fix volume collision in shared-machine compose: scope project name by `SIGNUM`.
|
||||||
|
- Auto-detect OS username (`$USER`) for volume isolation in own-account mode.
|
||||||
|
- Document the upgrade ritual for reconciling VM compose files.
|
||||||
|
- Add multi-user setup pointer in DOCKER_HUB.md.
|
||||||
|
|
||||||
|
## v1.14.20b — 2026-04-21
|
||||||
|
|
||||||
|
**Fix `[devbox]` prompt marker lost on `exec bash`.**
|
||||||
|
|
||||||
|
- The PS1 prefix guard used an exported env var that survived `exec bash`, but PS1 itself doesn't — so the new shell skipped adding the prefix. Replaced with a substring check on PS1 itself.
|
||||||
|
- Clarify tag-letter convention in AGENTS.md: suffix is the build ordinal, `a` is never used.
|
||||||
|
|
||||||
|
## v1.14.20 — 2026-04-21
|
||||||
|
|
||||||
|
**Opencode 1.14.20 + PROMPT_COMMAND/zoxide fix.**
|
||||||
|
|
||||||
|
- Bump opencode to 1.14.20.
|
||||||
|
- Fix `PROMPT_COMMAND` collision with zoxide: `history -a;` followed by zoxide's `;__zoxide_hook` produced `;;` which bash rejected on every prompt. Moved history-flush after zoxide init, using newline separator.
|
||||||
|
- Includes all v1.14.19c shell-defaults work (baked `.bash_aliases`/`.inputrc` via `/etc/skel-devbox/`, skel-copy on first run, `devbox-shell-history` named volume).
|
||||||
|
|
||||||
|
## v1.14.19d — 2026-04-21
|
||||||
|
|
||||||
|
*Superseded by v1.14.20 before building. Tagged but never built.*
|
||||||
|
|
||||||
|
## v1.14.19c — 2026-04-21
|
||||||
|
|
||||||
|
**Bash history persistence, shell defaults, GID auto-detect.**
|
||||||
|
|
||||||
|
- **Feature:** Bash history persists across `--force-recreate` via `devbox-shell-history` named volume at `~/.cache/bash`.
|
||||||
|
- **Feature:** Quality-of-life shell defaults shipped in `/etc/skel-devbox/` and copied to `~/` only if absent: prefix history search on Up/Down, 100k-entry timestamped dedup history, coloured case-insensitive tab completion, eza/bat aliases, zoxide/fzf integrations, `[devbox]` prompt marker.
|
||||||
|
- **Feature:** Skel-copy pattern — host bind-mounts and in-container customizations are never overwritten on upgrade.
|
||||||
|
- **Fix:** Entrypoint now detects workspace UID and GID independently. Hosts with UID 1000 but non-1000 GID (e.g. Debian's `useradd` default GID 1001) get correct group remapping.
|
||||||
|
- **Docs:** SSH banner-timeout troubleshooting (CGNAT), shell defaults section, skel restore/diff commands.
|
||||||
|
|
||||||
|
## v1.14.19b — 2026-04-20
|
||||||
|
|
||||||
|
**Ownership fixes and config/docs refresh.**
|
||||||
|
|
||||||
|
- **Fix:** Root-owned parent dirs left behind by nested named-volume mounts. Entrypoint now chowns `.local`, `.local/share`, `.local/state`, `.config` before leaf mount points.
|
||||||
|
- **Fix:** `deploy/sync-to-vm.sh` no longer preserves host GIDs (`rsync -a` → `-rlptDz`).
|
||||||
|
- Default model IDs refreshed (claude-sonnet-4-6, gpt-5.4, global Bedrock inference profile).
|
||||||
|
- Documentation gates oh-my-opencode-slim references to the OMOS variant.
|
||||||
|
|
||||||
|
## v1.14.19 — 2026-04-20
|
||||||
|
|
||||||
|
Bump opencode to 1.14.19.
|
||||||
|
|
||||||
|
## v1.14.18 — 2026-04-19
|
||||||
|
|
||||||
|
Fix Bun download URL: remove non-existent LATEST file fetch.
|
||||||
|
|
||||||
|
## v1.4.17 — 2026-04-19
|
||||||
|
|
||||||
|
Bump opencode to v1.4.17, add `file` utility to base image.
|
||||||
|
|
||||||
|
## v1.4.12 — 2026-04-18
|
||||||
|
|
||||||
|
Bump opencode to v1.4.12.
|
||||||
|
|
||||||
|
## v1.4.11 — 2026-04-18
|
||||||
|
|
||||||
|
Bump opencode to v1.4.11.
|
||||||
|
|
||||||
|
## v1.4.7 — 2026-04-17
|
||||||
|
|
||||||
|
Bump opencode to v1.4.7.
|
||||||
|
|
||||||
|
## v1.4.6 — 2026-04-15
|
||||||
|
|
||||||
|
Bump opencode to v1.4.6.
|
||||||
|
|
||||||
|
## v1.4.3k — 2026-04-13
|
||||||
|
|
||||||
|
Fix Bedrock config: add `AWS_PROFILE` to generated config, add `.agents/skills` to volume ownership fix.
|
||||||
|
|
||||||
|
## v1.4.3j — 2026-04-13
|
||||||
|
|
||||||
|
Upgrade base image from Debian bookworm to trixie (current stable). Bookworm EOL June 2026; trixie supported until 2028/LTS 2030.
|
||||||
|
|
||||||
|
## v1.4.3i — 2026-04-12
|
||||||
|
|
||||||
|
Add rustup for on-demand Rust support, document JS/TS development.
|
||||||
|
|
||||||
|
## v1.4.3h — 2026-04-12
|
||||||
|
|
||||||
|
Add uv package manager to base image for on-demand Python support.
|
||||||
|
|
||||||
|
## v1.4.3g — 2026-04-12
|
||||||
|
|
||||||
|
Fix IPv6 connectivity failures: force IPv4 preference in CI builds.
|
||||||
|
|
||||||
|
## v1.4.3f — 2026-04-11
|
||||||
|
|
||||||
|
Add error handling to Docker Hub description update step.
|
||||||
|
|
||||||
|
## v1.4.3e — 2026-04-10
|
||||||
|
|
||||||
|
Fix CVEs: install git-lfs from GitHub (Go 1.25), document Go versions for gosu/fzf.
|
||||||
|
|
||||||
|
## v1.4.3d — 2026-04-10
|
||||||
|
|
||||||
|
Fix CVEs: install gosu 1.19 and fzf 0.71.0 from GitHub releases instead of Debian packages.
|
||||||
|
|
||||||
|
## v1.4.3c — 2026-04-10
|
||||||
|
|
||||||
|
Fix CVEs: install gosu from GitHub release instead of Debian package (Go 1.19.8 → current).
|
||||||
|
|
||||||
|
## v1.4.3b — 2026-04-10
|
||||||
|
|
||||||
|
Fix entrypoint crash on read-only SSH mount.
|
||||||
|
|
||||||
|
## v1.4.3 — 2026-04-10
|
||||||
|
|
||||||
|
Bump opencode to 1.4.3.
|
||||||
|
|
||||||
|
## v1.4.2 — 2026-04-10
|
||||||
|
|
||||||
|
Initial release. Fix CI: use vars for username, secrets for token.
|
||||||
+11
-1
@@ -228,6 +228,10 @@ Understanding what survives container restarts and what doesn't:
|
|||||||
| `/home/developer/.local/share/opencode` | Named volume (if configured) | ✅ Yes — Docker volume | Session history, memory, auth tokens |
|
| `/home/developer/.local/share/opencode` | Named volume (if configured) | ✅ Yes — Docker volume | Session history, memory, auth tokens |
|
||||||
| `/home/developer/.local/state/opencode` | Named volume (if configured) | ✅ Yes — Docker volume | TUI settings (theme, toggles) |
|
| `/home/developer/.local/state/opencode` | Named volume (if configured) | ✅ Yes — Docker volume | TUI settings (theme, toggles) |
|
||||||
| `/home/developer/.cache/bash` | Named volume `devbox-shell-history` | ✅ Yes — Docker volume | Bash history (`$HISTFILE`) — survives container recreate |
|
| `/home/developer/.cache/bash` | Named volume `devbox-shell-history` | ✅ Yes — Docker volume | Bash history (`$HISTFILE`) — survives container recreate |
|
||||||
|
| `/home/developer/.local/share/zoxide` | Named volume `devbox-zoxide` | ✅ Yes — Docker volume | Zoxide directory history (`z <fragment>` jump targets) |
|
||||||
|
| `/home/developer/.local/share/nvim` | Named volume `devbox-nvim-data` | ✅ Yes — Docker volume | Neovim plugins, Mason LSP installs, Lazy plugin cache |
|
||||||
|
| `/home/developer/.mempalace` | Named volume `devbox-palace` (if configured) | ✅ Yes — Docker volume | MemPalace conversation memory, knowledge graph, embeddings |
|
||||||
|
| `/home/developer/.cache/chroma` | Named volume `devbox-chroma-cache` (if configured) | ✅ Yes — Docker volume | ChromaDB ONNX embedding model (~79 MB, downloaded on first use) |
|
||||||
| `/home/developer/.local/share/uv` | Named volume (if configured) | ✅ Yes — Docker volume | Python installs, uv tool installs |
|
| `/home/developer/.local/share/uv` | Named volume (if configured) | ✅ Yes — Docker volume | Python installs, uv tool installs |
|
||||||
| `/home/developer/.rustup` | Named volume (if configured) | ✅ Yes — Docker volume | Rust toolchains |
|
| `/home/developer/.rustup` | Named volume (if configured) | ✅ Yes — Docker volume | Rust toolchains |
|
||||||
| `/home/developer/.cargo` | Named volume (if configured) | ✅ Yes — Docker volume | Cargo binaries, registry cache |
|
| `/home/developer/.cargo` | Named volume (if configured) | ✅ Yes — Docker volume | Cargo binaries, registry cache |
|
||||||
@@ -486,7 +490,7 @@ To override with your host's own files, uncomment the matching bind-mount lines
|
|||||||
- **opencode** — AI coding assistant
|
- **opencode** — AI coding assistant
|
||||||
- **Node.js 22** — for npx-based MCP servers
|
- **Node.js 22** — for npx-based MCP servers
|
||||||
- **AWS CLI v2** — SSO and Bedrock authentication
|
- **AWS CLI v2** — SSO and Bedrock authentication
|
||||||
- **Dev tools** — git, git-lfs, git-crypt, age, ssh, ripgrep, fd, fzf, bat, eza, zoxide, uv, rustup, jq, make, gcc, g++, curl, wget, neovim 0.12, tmux, htop, tree, rsync
|
- **Dev tools** — git, git-lfs, git-crypt, age, ssh, ripgrep, fd, fzf, bat, eza, zoxide, uv, rustup, mempalace, gitea-mcp, jq, make, gcc, g++, curl, wget, neovim 0.12, tmux, htop, tree, rsync
|
||||||
- **Non-root user** — runs as `developer` with UID auto-matched to workspace owner (sudo available)
|
- **Non-root user** — runs as `developer` with UID auto-matched to workspace owner (sudo available)
|
||||||
|
|
||||||
### OMOS image (`latest-omos`)
|
### OMOS image (`latest-omos`)
|
||||||
@@ -547,6 +551,12 @@ ping all agents
|
|||||||
|
|
||||||
All six agents should respond if your provider authentication is working.
|
All six agents should respond if your provider authentication is working.
|
||||||
|
|
||||||
|
## Multi-User Setup
|
||||||
|
|
||||||
|
This guide covers single-user setup. For running multiple opencode-devbox instances in parallel — whether each user has their own OS account or everyone shares one login — see the [Multi-user setup section](https://gitea.jordbo.se/joakimp/opencode-devbox#multi-user-setup) in the source repository. It covers volume isolation, the `docker-compose.shared.yml` layout, and the `SIGNUM` / `$USER` auto-detection mechanism.
|
||||||
|
|
||||||
## Source
|
## Source
|
||||||
|
|
||||||
Build from source or contribute: [opencode-devbox on Gitea](https://gitea.jordbo.se/joakimp/opencode-devbox)
|
Build from source or contribute: [opencode-devbox on Gitea](https://gitea.jordbo.se/joakimp/opencode-devbox)
|
||||||
|
|
||||||
|
See the [Changelog](https://gitea.jordbo.se/joakimp/opencode-devbox/src/branch/main/CHANGELOG.md) for a full release history.
|
||||||
|
|||||||
+18
-1
@@ -5,7 +5,7 @@ ARG DEBIAN_VERSION=trixie-slim
|
|||||||
FROM debian:${DEBIAN_VERSION} AS base
|
FROM debian:${DEBIAN_VERSION} AS base
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG OPENCODE_VERSION=1.14.20
|
ARG OPENCODE_VERSION=1.14.28
|
||||||
|
|
||||||
LABEL maintainer="joakimp"
|
LABEL maintainer="joakimp"
|
||||||
LABEL description="Portable opencode developer container"
|
LABEL description="Portable opencode developer container"
|
||||||
@@ -42,9 +42,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
gcc \
|
gcc \
|
||||||
g++ \
|
g++ \
|
||||||
rsync \
|
rsync \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv \
|
||||||
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \
|
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# ── MemPalace — local-first AI memory system ─────────────────────────
|
||||||
|
# Provides semantic search over conversation history via 29 MCP tools.
|
||||||
|
# Palace data persists via the devbox-palace named volume.
|
||||||
|
# The embedding model (~300 MB) is downloaded on first use and cached
|
||||||
|
# in the palace directory.
|
||||||
|
RUN pip install --no-cache-dir --break-system-packages mempalace
|
||||||
|
|
||||||
# ── Go-compiled tools (install from GitHub to avoid CVEs in Debian's old Go builds)
|
# ── Go-compiled tools (install from GitHub to avoid CVEs in Debian's old Go builds)
|
||||||
|
|
||||||
# gosu — privilege de-escalation (built with Go 1.24.6)
|
# gosu — privilege de-escalation (built with Go 1.24.6)
|
||||||
@@ -113,6 +122,14 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
|
|||||||
curl -fsSL "https://static.rust-lang.org/rustup/dist/${ARCH}-unknown-linux-gnu/rustup-init" -o /usr/local/bin/rustup-init && \
|
curl -fsSL "https://static.rust-lang.org/rustup/dist/${ARCH}-unknown-linux-gnu/rustup-init" -o /usr/local/bin/rustup-init && \
|
||||||
chmod +x /usr/local/bin/rustup-init
|
chmod +x /usr/local/bin/rustup-init
|
||||||
|
|
||||||
|
# gitea-mcp — MCP server for Gitea API (official, Go binary)
|
||||||
|
ARG GITEA_MCP_VERSION=1.1.0
|
||||||
|
RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "arm64" ;; *) echo "x86_64" ;; esac) && \
|
||||||
|
curl -fsSL "https://gitea.com/gitea/gitea-mcp/releases/download/v${GITEA_MCP_VERSION}/gitea-mcp_Linux_${ARCH}.tar.gz" \
|
||||||
|
| tar -xz -C /usr/local/bin/ gitea-mcp && \
|
||||||
|
chmod +x /usr/local/bin/gitea-mcp && \
|
||||||
|
gitea-mcp --version
|
||||||
|
|
||||||
# Set locale — generate common UTF-8 locales (override via LANG/LC_ALL env vars)
|
# Set locale — generate common UTF-8 locales (override via LANG/LC_ALL env vars)
|
||||||
# To add more locales, run: sudo sed -i '/<locale>.UTF-8/s/^# //g' /etc/locale.gen && sudo locale-gen
|
# To add more locales, run: sudo sed -i '/<locale>.UTF-8/s/^# //g' /etc/locale.gen && sudo locale-gen
|
||||||
RUN sed -i -E '/(en_US|en_GB|sv_SE|da_DK|nb_NO|fi_FI|de_DE|fr_FR|es_ES|it_IT|pt_BR|nl_NL|pl_PL|ja_JP|ko_KR|zh_CN)\.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
RUN sed -i -E '/(en_US|en_GB|sv_SE|da_DK|nb_NO|fi_FI|de_DE|fr_FR|es_ES|it_IT|pt_BR|nl_NL|pl_PL|ja_JP|ko_KR|zh_CN)\.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
||||||
|
|||||||
@@ -273,11 +273,17 @@ volumes:
|
|||||||
- devbox-vscode:/home/developer/.vscode-server
|
- devbox-vscode:/home/developer/.vscode-server
|
||||||
```
|
```
|
||||||
|
|
||||||
### Shared machine setup (multiple users, single OS account)
|
### Multi-user setup
|
||||||
|
|
||||||
For machines where multiple users share one OS account (e.g. a common `garage` user), a separate compose file isolates each user's config and data using a `SIGNUM` variable.
|
The shared-machine compose file (`docker-compose.shared.yml`) supports two modes:
|
||||||
|
|
||||||
Each user creates their own directory and setup:
|
**Own-account mode** (each user has their own OS login — the common case):
|
||||||
|
Leave `SIGNUM` unset in `.env`. The project name defaults to `devbox-$USER`, so each OS user automatically gets isolated container names and named volumes with zero configuration.
|
||||||
|
|
||||||
|
**Shared-account mode** (everyone logs in as the same OS user, e.g. `garage`):
|
||||||
|
Each user sets `SIGNUM=<unique-id>` in `.env` to get isolation.
|
||||||
|
|
||||||
|
Setup per user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Replace <signum> with your username/identifier
|
# Replace <signum> with your username/identifier
|
||||||
@@ -291,17 +297,17 @@ cp /path/to/opencode-devbox/.env.shared.example .env
|
|||||||
# Create per-user config directory
|
# Create per-user config directory
|
||||||
mkdir -p ~/<signum>/.config/opencode
|
mkdir -p ~/<signum>/.config/opencode
|
||||||
|
|
||||||
# Edit .env with your signum, provider, keys, etc.
|
# Edit .env — set SIGNUM only if you're in shared-account mode
|
||||||
vim .env
|
vim .env
|
||||||
|
|
||||||
# Start
|
# Start
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
docker compose exec -u developer devbox-<signum> opencode
|
docker compose exec -u developer devbox opencode
|
||||||
```
|
```
|
||||||
|
|
||||||
Each user's container, config, and named volumes are fully isolated:
|
Each user's container, config, and named volumes are fully isolated:
|
||||||
- Container name: `devbox-<signum>` (no collisions)
|
- Container name: `devbox-<signum>` (or `devbox-$USER` in own-account mode)
|
||||||
- Named volumes: prefixed with the project directory name (automatic per-user isolation)
|
- Named volumes: prefixed with the project name (`devbox-<signum>_devbox-data`, etc.) — the Docker daemon is system-wide, so directory-name prefixing alone is NOT sufficient for isolation
|
||||||
- Opencode config: `~/<signum>/.config/opencode/` (per-user settings, OMOS config, etc.)
|
- Opencode config: `~/<signum>/.config/opencode/` (per-user settings, OMOS config, etc.)
|
||||||
|
|
||||||
See `docker-compose.shared.yml` and `.env.shared.example` for the full configuration.
|
See `docker-compose.shared.yml` and `.env.shared.example` for the full configuration.
|
||||||
@@ -438,6 +444,104 @@ The `--use-device-code` flag outputs a URL and short code instead of trying to o
|
|||||||
|
|
||||||
SSO sessions typically last 8–12 hours before requiring re-authentication. Since `~/.aws` is mounted from the host, tokens persist across container restarts.
|
SSO sessions typically last 8–12 hours before requiring re-authentication. Since `~/.aws` is mounted from the host, tokens persist across container restarts.
|
||||||
|
|
||||||
|
## MemPalace — persistent AI memory
|
||||||
|
|
||||||
|
The image includes [MemPalace](https://github.com/MemPalace/mempalace), a local-first AI memory system that stores conversation history verbatim and retrieves it via semantic search. Nothing leaves your machine.
|
||||||
|
|
||||||
|
### Enabling persistence
|
||||||
|
|
||||||
|
Uncomment the palace volume in `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- devbox-palace:/home/developer/.mempalace
|
||||||
|
```
|
||||||
|
|
||||||
|
Without the volume, palace data lives in the container's writable layer and is lost on `--force-recreate`.
|
||||||
|
|
||||||
|
### MCP integration with opencode
|
||||||
|
|
||||||
|
Add mempalace as an MCP server in your `opencode.json` (inside `~/.config/opencode/`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp": {
|
||||||
|
"mempalace": {
|
||||||
|
"type": "local",
|
||||||
|
"command": ["python3", "-m", "mempalace.mcp_server"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This gives opencode access to 29 MCP tools for searching memory, querying the knowledge graph, managing wings/rooms/drawers, and agent diaries.
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mine project files into the palace
|
||||||
|
mempalace mine /workspace
|
||||||
|
|
||||||
|
# Mine conversation transcripts
|
||||||
|
mempalace mine ~/.local/share/opencode/ --mode convos
|
||||||
|
|
||||||
|
# Search memory
|
||||||
|
mempalace search "why did we switch to eno1"
|
||||||
|
|
||||||
|
# Load context for a new session
|
||||||
|
mempalace wake-up
|
||||||
|
```
|
||||||
|
|
||||||
|
Each workspace gets its own isolated "wing" — memories never leak between projects.
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
Two separate named volumes keep different data classes apart:
|
||||||
|
|
||||||
|
- **Palace data** (`~/.mempalace/`): ChromaDB vectors, SQLite knowledge graph, drawers. This is your memory — back it up, treat it as precious. Persists via the `devbox-palace` named volume.
|
||||||
|
- **Embedding model cache** (`~/.cache/chroma/`): ONNX model (~79 MB), downloaded automatically on first search. Disposable — blow it away and it re-downloads in ~4 seconds. Persists via the `devbox-chroma-cache` named volume so you don't re-download on every container recreation.
|
||||||
|
- **No API keys required** for core functionality (local embeddings via ONNX).
|
||||||
|
|
||||||
|
Both volumes are commented out by default in `docker-compose.yml` — uncomment to enable:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- devbox-palace:/home/developer/.mempalace
|
||||||
|
- devbox-chroma-cache:/home/developer/.cache/chroma
|
||||||
|
```
|
||||||
|
|
||||||
|
**Air-gapped environments:** pre-populate the `devbox-chroma-cache` volume with the `all-MiniLM-L6-v2/` model contents. The palace volume needs no pre-population.
|
||||||
|
|
||||||
|
## Gitea MCP server
|
||||||
|
|
||||||
|
The image includes the [official Gitea MCP server](https://gitea.com/gitea/gitea-mcp) (`gitea-mcp`), providing 50+ MCP tools for interacting with self-hosted Gitea instances — repositories, issues, pull requests, releases, branches, wiki, and Actions.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
1. Create a Personal Access Token on your Gitea instance (Settings → Applications → Generate Token, scopes: `repo`, `read:user`).
|
||||||
|
|
||||||
|
2. Add to your `.env`:
|
||||||
|
```env
|
||||||
|
GITEA_HOST=https://your-gitea-instance.example.com
|
||||||
|
GITEA_ACCESS_TOKEN=your_token_here
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Enable the gitea MCP server in your `opencode.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp": {
|
||||||
|
"gitea": {
|
||||||
|
"type": "local",
|
||||||
|
"command": ["gitea-mcp", "-t", "stdio", "--host", "{env:GITEA_HOST}"],
|
||||||
|
"environment": {
|
||||||
|
"GITEA_ACCESS_TOKEN": "{env:GITEA_ACCESS_TOKEN}"
|
||||||
|
},
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The server is installed but disabled by default — it requires authentication to be useful.
|
||||||
|
|
||||||
## Shell defaults
|
## Shell defaults
|
||||||
|
|
||||||
The image ships a baked `.bash_aliases` and `.inputrc` with quality-of-life defaults. On first container start they are copied from `/etc/skel-devbox/` into `/home/developer/` **only if the target file does not already exist** — so host bind-mounts and any version you've customized inside the container are never overwritten on upgrade.
|
The image ships a baked `.bash_aliases` and `.inputrc` with quality-of-life defaults. On first container start they are copied from `/etc/skel-devbox/` into `/home/developer/` **only if the target file does not already exist** — so host bind-mounts and any version you've customized inside the container are never overwritten on upgrade.
|
||||||
@@ -460,6 +564,8 @@ Defaults you get out of the box:
|
|||||||
- ~/.inputrc:/home/developer/.inputrc:ro
|
- ~/.inputrc:/home/developer/.inputrc:ro
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Single-file bind-mount caveat (all platforms):** Docker bind-mounts the file's **inode**, not its path. When editors like vim, nvim, VS Code, or `sed -i` save a file, they write to a temp file and `rename()` it over the original — creating a new inode. The container stays pinned to the old (now unlinked) inode and never sees the update. This is a kernel limitation ([Docker #15793](https://github.com/moby/moby/issues/15793)), not fixable by Docker. Append-only writes (`echo "alias foo=bar" >> file`) are safe because they modify the same inode. **Workaround:** mount the parent directory instead of the single file (e.g. `~/.config/devbox-shell:/home/developer/.config/devbox-shell:ro`) and source files from there.
|
||||||
|
|
||||||
**Option B — customize inside the container.** Just edit `~/.bash_aliases` or `~/.inputrc` as normal. Pair this with a bind-mount or named volume on the home dir if you want the edits to survive container recreation.
|
**Option B — customize inside the container.** Just edit `~/.bash_aliases` or `~/.inputrc` as normal. Pair this with a bind-mount or named volume on the home dir if you want the edits to survive container recreation.
|
||||||
|
|
||||||
### Restoring or diffing defaults
|
### Restoring or diffing defaults
|
||||||
@@ -540,6 +646,8 @@ Container (Debian trixie)
|
|||||||
| `/home/developer/.local/share/opencode` | Named volume `devbox-data` | ✅ Yes | Session history, memory |
|
| `/home/developer/.local/share/opencode` | Named volume `devbox-data` | ✅ Yes | Session history, memory |
|
||||||
| `/home/developer/.local/state/opencode` | Named volume `devbox-state` | ✅ Yes | TUI settings (theme, toggles) |
|
| `/home/developer/.local/state/opencode` | Named volume `devbox-state` | ✅ Yes | TUI settings (theme, toggles) |
|
||||||
| `/home/developer/.cache/bash` | Named volume `devbox-shell-history` | ✅ Yes | Bash history (`$HISTFILE`), survives container recreate |
|
| `/home/developer/.cache/bash` | Named volume `devbox-shell-history` | ✅ Yes | Bash history (`$HISTFILE`), survives container recreate |
|
||||||
|
| `/home/developer/.local/share/zoxide` | Named volume `devbox-zoxide` | ✅ Yes | Zoxide directory history (`z <fragment>` jump targets) |
|
||||||
|
| `/home/developer/.local/share/nvim` | Named volume `devbox-nvim-data` | ✅ Yes | Neovim plugins, Mason LSP installs, Lazy plugin cache |
|
||||||
| `/home/developer/.local/share/uv` | Named volume `devbox-uv` (if configured) | ✅ Yes | Python installs, uv tool installs |
|
| `/home/developer/.local/share/uv` | Named volume `devbox-uv` (if configured) | ✅ Yes | Python installs, uv tool installs |
|
||||||
| `/home/developer/.rustup` | Named volume `devbox-rustup` (if configured) | ✅ Yes | Rust toolchains |
|
| `/home/developer/.rustup` | Named volume `devbox-rustup` (if configured) | ✅ Yes | Rust toolchains |
|
||||||
| `/home/developer/.cargo` | Named volume `devbox-cargo` (if configured) | ✅ Yes | Cargo binaries, registry cache |
|
| `/home/developer/.cargo` | Named volume `devbox-cargo` (if configured) | ✅ Yes | Cargo binaries, registry cache |
|
||||||
|
|||||||
@@ -198,6 +198,34 @@ After editing `docker-compose.yml` on the VM to uncomment the bind mounts you ne
|
|||||||
|
|
||||||
The script reads `docker-compose.yml` on the remote VM, detects which bind mounts are active, and syncs only those directories from your local machine. It also creates the remote directories if they don't exist.
|
The script reads `docker-compose.yml` on the remote VM, detects which bind mounts are active, and syncs only those directories from your local machine. It also creates the remote directories if they don't exist.
|
||||||
|
|
||||||
|
### Upgrading an existing VM to a new release
|
||||||
|
|
||||||
|
Each tagged release may add new named volumes or bind-mount lines to `docker-compose.yml`. Pulling a new image via `docker compose pull` grabs the new container behaviour, but compose files on the VM are user-owned and never touched by the image — you have to reconcile them yourself when upgrading across versions.
|
||||||
|
|
||||||
|
**Symptom of a missed reconcile:** a new feature quietly doesn't work even though the image is correct. Example from v1.14.19c → v1.14.20: bash history persistence requires the `devbox-shell-history` named volume mounted at `/home/developer/.cache/bash`. The v1.14.20 image writes history to that path either way, but without the volume mount on the VM, writes land in the container's writable layer and vanish on every `--force-recreate`.
|
||||||
|
|
||||||
|
**Upgrade ritual:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the VM, before recreating the container:
|
||||||
|
cd ~/opencode-devbox
|
||||||
|
cp docker-compose.yml docker-compose.yml.bak-$(date +%Y%m%d-%H%M%S)
|
||||||
|
|
||||||
|
# Compare against the repo version to see what's new:
|
||||||
|
# (from your local checkout)
|
||||||
|
scp devbox-affection:~/opencode-devbox/docker-compose.yml /tmp/vm-compose.yml
|
||||||
|
diff -u /tmp/vm-compose.yml ~/src/src_local/opencode-devbox/docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
For each new `volumes:` entry or mount line in the repo version that isn't in your VM's file, add it manually — preserving any local customizations you've made (image variant, read/write flags on bind mounts, etc.). Then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose config >/dev/null # verify YAML still parses
|
||||||
|
docker compose up -d --force-recreate
|
||||||
|
```
|
||||||
|
|
||||||
|
If you maintain the VM's compose file with no local changes, `scp` the repo version over wholesale. If you have customizations (the common case), do the diff-and-merge by hand.
|
||||||
|
|
||||||
### Shell defaults inside the container
|
### Shell defaults inside the container
|
||||||
|
|
||||||
The image ships baked `.bash_aliases` and `.inputrc` in `/etc/skel-devbox/` — quality-of-life defaults (prefix history search on Up/Down arrows, persistent history across container recreates via the `devbox-shell-history` named volume, `[devbox]` prompt marker, sensible aliases). On first container start the entrypoint copies them to `/home/developer/` **only if the target file does not already exist**.
|
The image ships baked `.bash_aliases` and `.inputrc` in `/etc/skel-devbox/` — quality-of-life defaults (prefix history search on Up/Down arrows, persistent history across container recreates via the `devbox-shell-history` named volume, `[devbox]` prompt marker, sensible aliases). On first container start the entrypoint copies them to `/home/developer/` **only if the target file does not already exist**.
|
||||||
@@ -210,6 +238,42 @@ This means:
|
|||||||
- To restore the baked defaults any time: `cp /etc/skel-devbox/.bash_aliases ~/` (or delete the file and recreate the container).
|
- To restore the baked defaults any time: `cp /etc/skel-devbox/.bash_aliases ~/` (or delete the file and recreate the container).
|
||||||
- To diff your current config against what the image ships: `diff ~/.bash_aliases /etc/skel-devbox/.bash_aliases`.
|
- To diff your current config against what the image ships: `diff ~/.bash_aliases /etc/skel-devbox/.bash_aliases`.
|
||||||
|
|
||||||
|
### CI runner maintenance: automatic Docker pruning
|
||||||
|
|
||||||
|
Gitea Actions runners accumulate Docker build cache, stale buildkit containers, and unused images over time. Without periodic cleanup, the runner's disk fills up and builds stall during the image-push phase (symptom: `#61 exporting to image` / `pushing layers` hangs indefinitely while buildkit repeatedly re-authenticates with Docker Hub).
|
||||||
|
|
||||||
|
Set up two layers of automatic cleanup on the runner host:
|
||||||
|
|
||||||
|
**1. Daily cron job** — prunes images, containers, and build cache older than 72 hours:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo tee /etc/cron.daily/docker-prune <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
docker system prune -af --filter "until=72h" > /var/log/docker-prune.log 2>&1
|
||||||
|
docker builder prune -af --filter "until=72h" >> /var/log/docker-prune.log 2>&1
|
||||||
|
EOF
|
||||||
|
sudo chmod +x /etc/cron.daily/docker-prune
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Docker daemon builder GC** — caps buildkit cache at 10 GB (Docker 23.0+):
|
||||||
|
|
||||||
|
Add to `/etc/docker/daemon.json` (create if absent):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"builder": {
|
||||||
|
"gc": {
|
||||||
|
"enabled": true,
|
||||||
|
"defaultKeepStorage": "10GB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `sudo systemctl restart docker`.
|
||||||
|
|
||||||
|
Both are safe to run on a machine that also hosts long-running containers (like opencode-devbox) — `docker system prune` only removes *unused* images and *stopped* containers, never running ones.
|
||||||
|
|
||||||
### Troubleshooting: SSH hangs or "banner exchange" timeouts
|
### Troubleshooting: SSH hangs or "banner exchange" timeouts
|
||||||
|
|
||||||
If SSH to the VM intermittently fails with `Connection timed out during banner exchange` or pure TCP connect timeouts — especially after the first few successful connects in a short window — the cause is almost certainly your ISP's CGNAT (Carrier-Grade NAT), not the VM.
|
If SSH to the VM intermittently fails with `Connection timed out during banner exchange` or pure TCP connect timeouts — especially after the first few successful connects in a short window — the cause is almost certainly your ISP's CGNAT (Carrier-Grade NAT), not the VM.
|
||||||
|
|||||||
@@ -12,20 +12,32 @@
|
|||||||
# 5. mkdir -p ~/<signum>/.config/opencode
|
# 5. mkdir -p ~/<signum>/.config/opencode
|
||||||
# 6. docker compose up -d
|
# 6. docker compose up -d
|
||||||
#
|
#
|
||||||
# Named volumes are automatically isolated per user because Docker Compose
|
# Volume isolation: the top-level 'name:' field derives a unique project
|
||||||
# prefixes them with the project directory name (e.g. opencode-devbox_devbox-data).
|
# name per user, which Docker Compose uses as the prefix for all named
|
||||||
# Since each user runs from ~/<signum>/opencode-devbox/, volumes don't collide.
|
# volumes. Without this, two users whose compose file lives in a directory
|
||||||
|
# with the same basename would share volumes — the Docker daemon is
|
||||||
|
# system-wide and doesn't scope by OS user.
|
||||||
|
#
|
||||||
|
# Two modes:
|
||||||
|
# Own-account mode (each user has their own OS login):
|
||||||
|
# Leave SIGNUM unset in .env — it defaults to $USER automatically.
|
||||||
|
# Shared-account mode (everyone logs in as the same OS user):
|
||||||
|
# Set SIGNUM=<unique-id> in .env so each person gets isolated volumes.
|
||||||
|
|
||||||
|
name: devbox-${SIGNUM:-${USER}}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
devbox:
|
devbox:
|
||||||
image: joakimp/opencode-devbox:latest
|
image: joakimp/opencode-devbox:latest
|
||||||
container_name: devbox-${SIGNUM:?Set SIGNUM in .env}
|
container_name: devbox-${SIGNUM:-${USER}}
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
- TERM=xterm-256color
|
- TERM=xterm-256color
|
||||||
|
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
||||||
|
- GITEA_HOST=${GITEA_HOST:-}
|
||||||
volumes:
|
volumes:
|
||||||
# Host workspace — user's project directory
|
# Host workspace — user's project directory
|
||||||
- ${WORKSPACE_PATH:-~/src}:/workspace
|
- ${WORKSPACE_PATH:-~/src}:/workspace
|
||||||
@@ -42,13 +54,29 @@ services:
|
|||||||
# Persist bash history across container recreations
|
# Persist bash history across container recreations
|
||||||
- devbox-shell-history:/home/developer/.cache/bash
|
- devbox-shell-history:/home/developer/.cache/bash
|
||||||
|
|
||||||
|
# Persist zoxide directory history ('z <fragment>' to jump)
|
||||||
|
- devbox-zoxide:/home/developer/.local/share/zoxide
|
||||||
|
|
||||||
|
# Persist neovim plugin/Mason data (avoids re-downloading on every recreate)
|
||||||
|
- devbox-nvim-data:/home/developer/.local/share/nvim
|
||||||
|
|
||||||
# Persist uv data (Python installs)
|
# Persist uv data (Python installs)
|
||||||
- devbox-uv:/home/developer/.local/share/uv
|
- devbox-uv:/home/developer/.local/share/uv
|
||||||
|
|
||||||
|
# Optional: persist MemPalace data (conversation memory, knowledge graph)
|
||||||
|
# - devbox-palace:/home/developer/.mempalace
|
||||||
|
|
||||||
|
# Optional: persist ChromaDB embedding model cache (~79 MB)
|
||||||
|
# - devbox-chroma-cache:/home/developer/.cache/chroma
|
||||||
|
|
||||||
# Optional: AWS credentials (per-user if available)
|
# Optional: AWS credentials (per-user if available)
|
||||||
# - ${HOME}/${SIGNUM}/.aws:/home/developer/.aws
|
# - ${HOME}/${SIGNUM}/.aws:/home/developer/.aws
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
devbox-data:
|
devbox-data:
|
||||||
devbox-shell-history:
|
devbox-shell-history:
|
||||||
|
devbox-zoxide:
|
||||||
|
devbox-nvim-data:
|
||||||
devbox-uv:
|
devbox-uv:
|
||||||
|
# devbox-palace:
|
||||||
|
# devbox-chroma-cache:
|
||||||
|
|||||||
+36
-1
@@ -8,6 +8,11 @@
|
|||||||
# Or for interactive one-shot:
|
# Or for interactive one-shot:
|
||||||
# docker compose run --rm devbox
|
# docker compose run --rm devbox
|
||||||
|
|
||||||
|
# Pin the project name so named volumes survive directory renames.
|
||||||
|
# Without this, Docker Compose derives the project name from the
|
||||||
|
# directory basename — renaming the dir orphans all existing volumes.
|
||||||
|
name: opencode-devbox
|
||||||
|
|
||||||
services:
|
services:
|
||||||
devbox:
|
devbox:
|
||||||
image: joakimp/opencode-devbox:latest
|
image: joakimp/opencode-devbox:latest
|
||||||
@@ -28,6 +33,9 @@ services:
|
|||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
- TERM=xterm-256color
|
- TERM=xterm-256color
|
||||||
|
- GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN:-}
|
||||||
|
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
|
||||||
|
- GITEA_HOST=${GITEA_HOST:-}
|
||||||
volumes:
|
volumes:
|
||||||
# Host workspace — mount your project here
|
# Host workspace — mount your project here
|
||||||
- ${WORKSPACE_PATH:-.}:/workspace
|
- ${WORKSPACE_PATH:-.}:/workspace
|
||||||
@@ -56,11 +64,21 @@ services:
|
|||||||
# Without this, ~/.bash_history is lost on 'docker compose up --force-recreate'.
|
# Without this, ~/.bash_history is lost on 'docker compose up --force-recreate'.
|
||||||
- devbox-shell-history:/home/developer/.cache/bash
|
- devbox-shell-history:/home/developer/.cache/bash
|
||||||
|
|
||||||
|
# Persist zoxide directory history ('z <fragment>' to jump).
|
||||||
|
- devbox-zoxide:/home/developer/.local/share/zoxide
|
||||||
|
|
||||||
# Optional: override baked shell defaults with your host's rc files.
|
# Optional: override baked shell defaults with your host's rc files.
|
||||||
# The image ships sensible defaults (history tuning, prefix-search on
|
# The image ships sensible defaults (history tuning, prefix-search on
|
||||||
# Up/Down arrows, fzf/zoxide integration). Uncomment to use your own:
|
# Up/Down arrows, fzf/zoxide integration). Uncomment to use your own:
|
||||||
|
#
|
||||||
|
# NOTE: Single-file bind-mounts break when editors use atomic save
|
||||||
|
# (vim, VS Code, sed -i write a temp file then rename() over the
|
||||||
|
# original, creating a new inode the container never sees). This is a
|
||||||
|
# kernel limitation, not Docker-specific. If host edits stop appearing
|
||||||
|
# in the container, mount the parent directory instead — see the
|
||||||
|
# "Shell defaults" section in README.md.
|
||||||
# - ~/.bash_aliases:/home/developer/.bash_aliases:ro
|
# - ~/.bash_aliases:/home/developer/.bash_aliases:ro
|
||||||
# - ~/.inputrc:/home/developer/.inputrc:ro
|
# - ~/.inputrc:/home/developer/.inputrc:ro
|
||||||
|
|
||||||
# Optional: persist uv data (Python installs, tool installs)
|
# Optional: persist uv data (Python installs, tool installs)
|
||||||
# Without this, 'uv python install' must be re-run after container removal.
|
# Without this, 'uv python install' must be re-run after container removal.
|
||||||
@@ -74,6 +92,19 @@ services:
|
|||||||
# Optional: persist VS Code server and extensions across container recreations
|
# Optional: persist VS Code server and extensions across container recreations
|
||||||
# - devbox-vscode:/home/developer/.vscode-server
|
# - devbox-vscode:/home/developer/.vscode-server
|
||||||
|
|
||||||
|
# Persist neovim plugin/Mason data (avoids re-downloading on every recreate)
|
||||||
|
- devbox-nvim-data:/home/developer/.local/share/nvim
|
||||||
|
|
||||||
|
# Optional: persist MemPalace data (conversation memory, knowledge graph,
|
||||||
|
# embeddings). Without this, palace data is lost on container recreation.
|
||||||
|
# - devbox-palace:/home/developer/.mempalace
|
||||||
|
|
||||||
|
# Optional: persist ChromaDB embedding model cache (~79 MB, downloaded on
|
||||||
|
# first mempalace search). Without this, the model re-downloads on every
|
||||||
|
# container recreation. Separate from palace data — model cache is
|
||||||
|
# disposable, palace data is precious.
|
||||||
|
# - devbox-chroma-cache:/home/developer/.cache/chroma
|
||||||
|
|
||||||
# Optional: AWS credentials/SSO config (not read-only — SSO writes token cache)
|
# Optional: AWS credentials/SSO config (not read-only — SSO writes token cache)
|
||||||
# - ~/.aws:/home/developer/.aws
|
# - ~/.aws:/home/developer/.aws
|
||||||
|
|
||||||
@@ -81,7 +112,11 @@ volumes:
|
|||||||
devbox-data:
|
devbox-data:
|
||||||
devbox-state:
|
devbox-state:
|
||||||
devbox-shell-history:
|
devbox-shell-history:
|
||||||
|
devbox-zoxide:
|
||||||
|
devbox-nvim-data:
|
||||||
devbox-uv:
|
devbox-uv:
|
||||||
|
# devbox-palace:
|
||||||
|
# devbox-chroma-cache:
|
||||||
# devbox-rustup:
|
# devbox-rustup:
|
||||||
# devbox-cargo:
|
# devbox-cargo:
|
||||||
# devbox-vscode:
|
# devbox-vscode:
|
||||||
|
|||||||
@@ -15,6 +15,17 @@ if [ -d "$SKEL_DIR" ]; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── MemPalace: initialize palace for the workspace if mempalace is installed
|
||||||
|
# Creates the palace directory structure on first run. Idempotent — skips
|
||||||
|
# if palace already exists. Uses the workspace path to derive the wing name.
|
||||||
|
if command -v mempalace &>/dev/null && [ -d /workspace ]; then
|
||||||
|
PALACE_DIR="${HOME}/.mempalace"
|
||||||
|
if [ ! -d "$PALACE_DIR/palace" ]; then
|
||||||
|
echo "Initializing MemPalace for workspace..."
|
||||||
|
mempalace init /workspace 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Git config defaults ──────────────────────────────────────────────
|
# ── Git config defaults ──────────────────────────────────────────────
|
||||||
if [ -n "${GIT_USER_NAME:-}" ] && ! git config --global user.name &>/dev/null; then
|
if [ -n "${GIT_USER_NAME:-}" ] && ! git config --global user.name &>/dev/null; then
|
||||||
git config --global user.name "$GIT_USER_NAME"
|
git config --global user.name "$GIT_USER_NAME"
|
||||||
@@ -81,6 +92,35 @@ EOF
|
|||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Add MCP servers for installed tools
|
||||||
|
if command -v python3 &>/dev/null; then
|
||||||
|
python3 -c "
|
||||||
|
import json, shutil
|
||||||
|
with open('$CONFIG_FILE') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
mcp = config.setdefault('mcp', {})
|
||||||
|
|
||||||
|
# MemPalace — local AI memory (if installed)
|
||||||
|
if shutil.which('mempalace'):
|
||||||
|
mcp['mempalace'] = {
|
||||||
|
'type': 'local',
|
||||||
|
'command': ['python3', '-m', 'mempalace.mcp_server']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gitea — self-hosted Git forge API (if installed)
|
||||||
|
if shutil.which('gitea-mcp'):
|
||||||
|
mcp['gitea'] = {
|
||||||
|
'type': 'local',
|
||||||
|
'command': ['gitea-mcp', '-t', 'stdio'],
|
||||||
|
'enabled': False
|
||||||
|
}
|
||||||
|
|
||||||
|
with open('$CONFIG_FILE', 'w') as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
f.write('\n')
|
||||||
|
" 2>/dev/null && echo "MCP servers registered in opencode config."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── oh-my-opencode-slim setup (multi-agent orchestration) ────────────
|
# ── oh-my-opencode-slim setup (multi-agent orchestration) ────────────
|
||||||
|
|||||||
@@ -77,7 +77,11 @@ for dir in \
|
|||||||
/home/"$USER_NAME"/.local/share/opencode \
|
/home/"$USER_NAME"/.local/share/opencode \
|
||||||
/home/"$USER_NAME"/.local/state/opencode \
|
/home/"$USER_NAME"/.local/state/opencode \
|
||||||
/home/"$USER_NAME"/.local/share/uv \
|
/home/"$USER_NAME"/.local/share/uv \
|
||||||
|
/home/"$USER_NAME"/.local/share/zoxide \
|
||||||
|
/home/"$USER_NAME"/.local/share/nvim \
|
||||||
|
/home/"$USER_NAME"/.mempalace \
|
||||||
/home/"$USER_NAME"/.cache/bash \
|
/home/"$USER_NAME"/.cache/bash \
|
||||||
|
/home/"$USER_NAME"/.cache/chroma \
|
||||||
/home/"$USER_NAME"/.rustup \
|
/home/"$USER_NAME"/.rustup \
|
||||||
/home/"$USER_NAME"/.cargo \
|
/home/"$USER_NAME"/.cargo \
|
||||||
/home/"$USER_NAME"/.vscode-server \
|
/home/"$USER_NAME"/.vscode-server \
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
# To override, bind-mount your host's ~/.bash_aliases over this file
|
# To override, bind-mount your host's ~/.bash_aliases over this file
|
||||||
# via docker-compose.yml.
|
# via docker-compose.yml.
|
||||||
|
|
||||||
|
# ── Host-shared shell customizations (devbox-shell bridge) ───────────
|
||||||
|
# If the host bind-mounts a directory at ~/.config/devbox-shell/ (the
|
||||||
|
# recommended pattern for sharing aliases/PATH/utilities between host
|
||||||
|
# and container), source the bash_aliases file from it. This survives
|
||||||
|
# --force-recreate because it's baked into the image's skel, not the
|
||||||
|
# container's writable layer. Hosts that don't use this pattern are
|
||||||
|
# unaffected — the test silently skips if the file doesn't exist.
|
||||||
|
[ -r "$HOME/.config/devbox-shell/bash_aliases" ] && . "$HOME/.config/devbox-shell/bash_aliases"
|
||||||
|
|
||||||
# ── History persistence and quality ──────────────────────────────────
|
# ── History persistence and quality ──────────────────────────────────
|
||||||
# The named volume devbox-shell-history is mounted at ~/.cache/bash
|
# The named volume devbox-shell-history is mounted at ~/.cache/bash
|
||||||
# so history survives container recreation.
|
# so history survives container recreation.
|
||||||
@@ -76,7 +85,10 @@ fi
|
|||||||
|
|
||||||
# ── Prompt: show [opencode-devbox] tag so it's obvious you're in the container
|
# ── Prompt: show [opencode-devbox] tag so it's obvious you're in the container
|
||||||
# Preserves the default Debian PS1 logic but prefixes with a container marker.
|
# Preserves the default Debian PS1 logic but prefixes with a container marker.
|
||||||
if [ -n "${PS1:-}" ] && [ -z "${DEVBOX_PS1_SET:-}" ]; then
|
# We check for the literal '[devbox]' substring in PS1 rather than relying on
|
||||||
|
# an exported guard variable — otherwise `exec bash` inherits the guard but
|
||||||
|
# gets a fresh (prefix-less) PS1 from .bashrc, and the prefix would never be
|
||||||
|
# re-added in the new shell.
|
||||||
|
if [ -n "${PS1:-}" ] && [[ "$PS1" != *"[devbox]"* ]]; then
|
||||||
PS1='\[\e[38;5;39m\][devbox]\[\e[0m\] '"${PS1}"
|
PS1='\[\e[38;5;39m\][devbox]\[\e[0m\] '"${PS1}"
|
||||||
export DEVBOX_PS1_SET=1
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user