Skip to content

Add OpenCode sandbox workflows#3

Open
christestet wants to merge 3 commits into
mainfrom
feat/cleanup-hook
Open

Add OpenCode sandbox workflows#3
christestet wants to merge 3 commits into
mainfrom
feat/cleanup-hook

Conversation

@christestet
Copy link
Copy Markdown
Collaborator

@christestet christestet commented May 23, 2026

What changed

This adds a more reliable OpenCode sandbox workflow setup for local vLLM-backed usage:

  • Added global sandbox rules in config/AGENTS.md.
  • Added reusable OpenCode commands:
    • /refactor-audit <target> for read-only refactor analysis.
    • /refactor-apply <approved scope> for focused refactors with verification and WORKLOG.md updates.
    • /git-commit for reviewed Conventional Commit workflows.
  • Updated the write-worklog skill so it is an on-demand formatting helper rather than a second global rule source.
  • Kept HOME=/home/opencode and changed the entrypoint to start OpenCode from /home/opencode/workspace without moving the home directory.
  • Simplified Compose mounts for global commands, skills, and rules.
  • Added ripgrep and tzdata to the image so OpenCode search tooling and Europe/Berlin timestamps work without runtime downloads or UTC fallback.
  • Updated README documentation to match the current Qwen/vLLM config, mount layout, command behavior, and sandbox expectations.

Why

The previous setup mixed persistent rules, skills, and command behavior in ways that were harder for local models to follow reliably. In testing, broad AGENTS.md instructions were useful for orientation, but repeatable workflow requirements were followed more consistently when embedded directly in slash commands.

This keeps the layers clearer:

  • AGENTS.md provides short global sandbox orientation.
  • Commands define repeatable workflows and required worklog behavior.
  • Skills provide on-demand reusable formatting and detailed procedures.
  • Docker/Compose provide the actual filesystem and runtime boundary.

Validation

Configuration and syntax checks:

jq empty config/opencode.json config/opencode-with-vllm-side.json
docker compose config
bash -n entrypoint.sh start.sh

Container/runtime checks performed:

docker compose build
docker compose run --rm opencode --version
docker compose run --rm --entrypoint bash opencode -lc 'rg --version | head -n 1; TZ=Europe/Berlin date "+%d.%m.%Y, %H:%M (%Z)"'

Observed results:

  • OpenCode started successfully and reported 1.15.10.
  • ripgrep 15.1.0 was available in the container.
  • Europe/Berlin timestamp resolved correctly as CEST.

Workflow smoke test performed in a temporary workspace project:

docker compose run --rm opencode run \
  --dir /home/opencode/workspace/opencode-agent-follow-test \
  --command refactor-apply \
  "In calc.py, extract the duplicated label normalization steps from normalize_user_label and normalize_project_label into a private helper named _normalize_label. Keep behavior unchanged. Do not touch any other code." \
  --format default

Observed result:

  • calc.py was refactored as requested.
  • WORKLOG.md was updated with the changed file, findings, CEST timestamp, and pending state.

A normal non-command OpenCode run was also tested with a small file edit and updated WORKLOG.md as expected.

Notes

/refactor-audit was recognized as the plan agent and remained read-only, but the local model stalled during one audit smoke test. The command wiring was correct; the stall appears related to local model/tool-loop behavior rather than configuration.

Summary by CodeRabbit

  • Documentation

    • Added comprehensive sandbox rules and agent guidelines.
    • Introduced command workflow specifications for commits and refactors.
    • Updated worklog writing standards and formats.
    • Enhanced setup and configuration documentation.
  • New Features

    • Added reset-sandbox utility script for resetting sandbox state.
  • Chores

    • Updated Docker and compose configurations for improved workspace management.

@christestet
Copy link
Copy Markdown
Collaborator Author

christestet commented May 23, 2026

here is calc.py

def normalize_user_label(label):
      value = label.strip()
      value = value.lower()
      value = value.replace(" ", "-")
      return value

      return value


  def format_status(name, enabled):
      if enabled:
          status = "enabled"
      else:
          status = "disabled"

      return f"{normalize_user_label(name)}:{status}"

@christestet christestet requested a review from jammsen May 23, 2026 14:28
Comment thread .opencode/commands/refactor-audit.md Outdated
Comment thread .opencode/skills/write-worklog/SKILL.md Outdated
Comment thread config/AGENTS.md Outdated
Comment thread config/AGENTS.md Outdated
Comment thread config/AGENTS.md Outdated

Use the mounted sandbox commands for repeatable workflows when they fit the task:

- `/refactor-audit <target>`: inspect a target for refactor opportunities without editing files.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a target? Does the LLM know that? Im confused

Comment thread README.md Outdated
# A hardened Docker sandbox setup for opencode

With OpenCode + vLLM (Gemma 4 26B MoE) configs
With OpenCode + vLLM local model configs
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name the model (qwen3.6-35b) is more descriptive for people cloning the repo, then hiding it inside the config inside a nested directory.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this for now - may we should have a sperate section for linking all related parts and files where the llm is addressed

Comment thread README.md
Comment thread README.md
Comment thread README.md
1. Press `/model` — your model should appear under your provider name with an orange dot
2. Type `hello, what model are you?` — the response should mention your model ID
3. Check the status bar at the bottom — it should show `Gemma 4 26B MoE · vLLM (Gemma4 local)`
3. Check the status bar at the bottom — it should show your configured model, for example `Qwen3.6 35B A3B · vLLM`
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice, the previous part should be named just like that.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean "Qwen3.6 35B A3B"

Comment thread README.md Outdated
```

All runtimes are installed at **build time** under the `opencode` user, so the container starts instantly with no network downloads at runtime. The tool binaries are on `PATH` and their data directories (`CARGO_HOME`, `RUSTUP_HOME`) are pinned via environment variables so they survive the `HOME` override that redirects opencode's session state to the mounted workspace.
All runtimes are installed at **build time** under the `opencode` user, so the container starts instantly with no language runtime downloads at startup. Base tooling includes `ripgrep` for OpenCode search tools and `tzdata` for correct Europe/Berlin timestamps. The tool binaries are on `PATH` and their data directories (`CARGO_HOME`, `RUSTUP_HOME`) are pinned via environment variables.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove "so they survive the HOME override that redirects opencode's session state to the mounted workspace."?

Comment thread config/opencode.json Outdated
"agent": {
"build": {
"prompt": "All project are located under /home/opencode/workspace, you will only work there and create a new subdirectory for each task or project, NO EXCEPTIONS. You must maintain a file called WORKLOG.md in the current project working directory at all times.\\n\\nRules:\\n- Before starting any task, check if WORKLOG.md exists and read it to understand prior context.\\n- After completing EACH task, immediately update WORKLOG.md with: what was done (specific files changed, exact lines modified), what was found (exact issues, not vague summaries), and what still needs doing with current date and time in german timezone. Do NOT proceed to the next task until WORKLOG.md has been updated.\\n- If WORKLOG.md does not exist, create it before doing anything else.",
"prompt": "Follow the sandbox rules from /home/opencode/.config/opencode/AGENTS.md. Before finishing any task that changes files, read WORKLOG.md if present and append a concise entry with the current Europe/Berlin timestamp from `TZ=Europe/Berlin date \"+%d.%m.%Y, %H:%M (%Z)\"`, changed files, concrete findings, and pending follow-ups.",
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be kept

"All project are located under /home/opencode/workspace, you will only work there and create a new subdirectory for each task or project, NO EXCEPTIONS."

Comment thread scripts/cleanup.sh Outdated
@christestet christestet marked this pull request as ready for review June 6, 2026 04:45
@jammsen
Copy link
Copy Markdown
Owner

jammsen commented Jun 6, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

✅ Action performed

Full review finished.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a complete OpenCode sandbox framework by establishing agent constraints, command specifications, container configuration, and user documentation. It defines how agents should operate within /home/opencode/workspace, what commands are available for task workflows, how the container is built and composed to support these workflows, and provides utilities and documentation for end users.

Changes

Sandbox Rules, Commands, and Runtime

Layer / File(s) Summary
Sandbox Rules and Agent Configuration
config/AGENTS.md, config/opencode.json, config/opencode-with-vllm-side.json
AGENTS.md establishes workspace constraints and available sandbox commands. Agent build prompts in both opencode.json and opencode-with-vllm-side.json are updated to enforce sandbox rules, require reading existing WORKLOG.md, and mandate appending timestamped entries with Europe/Berlin timestamps and concrete file-change/findings/pending details.
Command and Skill Specifications
.opencode/commands/git-commit.md, .opencode/commands/refactor-apply.md, .opencode/commands/refactor-audit.md, .opencode/skills/write-worklog/SKILL.md
Three new commands (/git-commit, /refactor-apply, /refactor-audit) are specified with structured workflows; the write-worklog skill is updated with exact timestamp formatting using TZ=Europe/Berlin, structured "What Changed" sections (file-level and line-specific bullets), "Findings" and "Pending" sections, and an "Avoid" section clarifying constraints on relative timestamps and workspace-only updates.
Container Runtime and Tooling Setup
Dockerfile, compose.yml, entrypoint.sh
Dockerfile disables Node.js by default (ENABLE_NODEJS=false) and adds ca-certificates, ripgrep, and tzdata to base packages. Compose mounts sandbox commands/skills and AGENTS.md as read-only in container. Entrypoint exports OPENCODE_WORKSPACE and HOME to workspace directory before starting opencode.
User Documentation and Sandbox Utilities
README.md, scripts/reset-sandbox.sh
README is updated to reference Qwen model setup example, clarify workspace/data directory structure and read-only mounts, add explicit "Resetting sandbox state" section documenting scripts/reset-sandbox.sh behavior, and tighten filesystem access restrictions. New reset-sandbox.sh script safely deletes workspace/data contents with confirmation, preserving .gitkeep placeholders and supporting --yes and --help flags.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A sandbox for the code so fine,
With commands that run on Berlin time,
Worklogs append with care and grace,
While agents toil in their workspace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add OpenCode sandbox workflows' accurately summarizes the main change: introducing sandbox workflows with commands, configuration, and tooling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cleanup-hook

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

138-138: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix compose filename in troubleshooting text.

Line 138 references docker-compose.yml, but this repo uses compose.yml. The current wording can mislead users during config checks.

Suggested fix
-If this returns an error, check that `docker compose` is run from the same directory as `docker-compose.yml` and that `./config/opencode.json` exists.
+If this returns an error, check that `docker compose` is run from the same directory as `compose.yml` and that `./config/opencode.json` exists.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 138, Update the troubleshooting sentence that currently
mentions `docker-compose.yml` to reference the correct compose filename
`compose.yml`; locate the exact line string "check that `docker compose` is run
from the same directory as `docker-compose.yml` and that
`./config/opencode.json` exists" and replace `docker-compose.yml` with
`compose.yml` so the guidance matches this repository's actual file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.opencode/commands/git-commit.md:
- Around line 18-21: The WORKLOG.md step requires a commit hash that doesn't
exist yet (see step "8. Append a concise entry to `WORKLOG.md`" and the template
expecting a hash), so reorder the steps so the commit is created before
appending the WORKLOG entry (i.e., perform steps 10–11 to make the commit and
capture the hash, then append the WORKLOG.md entry including that hash, then
stage/commit the WORKLOG if necessary), or alternatively change the WORKLOG
template to omit the commit hash pre-commit; update the sequence around
`WORKLOG.md`, the conventional commit instruction example, and the post-commit
status display to reflect the new ordering.

In `@entrypoint.sh`:
- Around line 54-58: OPENCODE_WORKSPACE is only assigned and not exported so the
launched process (exec gosu $APP_USER:$APP_GROUP opencode) won't receive it;
export OPENCODE_WORKSPACE="/home/opencode/workspace" (before exporting HOME and
before the exec line) so the environment variable is available to the opencode
process and any downstream commands that depend on it.

---

Outside diff comments:
In `@README.md`:
- Line 138: Update the troubleshooting sentence that currently mentions
`docker-compose.yml` to reference the correct compose filename `compose.yml`;
locate the exact line string "check that `docker compose` is run from the same
directory as `docker-compose.yml` and that `./config/opencode.json` exists" and
replace `docker-compose.yml` with `compose.yml` so the guidance matches this
repository's actual file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eed6b390-7754-4305-b521-e1b660930756

📥 Commits

Reviewing files that changed from the base of the PR and between 1ff1d05 and ec71bd4.

📒 Files selected for processing (12)
  • .opencode/commands/git-commit.md
  • .opencode/commands/refactor-apply.md
  • .opencode/commands/refactor-audit.md
  • .opencode/skills/write-worklog/SKILL.md
  • Dockerfile
  • README.md
  • compose.yml
  • config/AGENTS.md
  • config/opencode-with-vllm-side.json
  • config/opencode.json
  • entrypoint.sh
  • scripts/reset-sandbox.sh

Comment on lines +18 to +21
8. Append a concise entry to `WORKLOG.md` before committing.
9. Stage only approved files.
10. Use a concise Conventional Commit message, for example `feat: add sandbox commands` or `fix: preserve opencode home path`.
11. After committing, show the commit hash and final `git status --short`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix impossible ordering for commit hash in WORKLOG entry.

The workflow requires writing WORKLOG.md before commit, but the template requires a commit hash that only exists after commit. Reorder steps (commit first, then append worklog) or remove hash from the pre-commit entry shape.

Suggested minimal correction
-8. Append a concise entry to `WORKLOG.md` before committing.
-9. Stage only approved files.
-10. Use a concise Conventional Commit message, for example `feat: add sandbox commands` or `fix: preserve opencode home path`.
-11. After committing, show the commit hash and final `git status --short`.
+8. Stage only approved files.
+9. Use a concise Conventional Commit message, for example `feat: add sandbox commands` or `fix: preserve opencode home path`.
+10. Commit the changes.
+11. Append a concise entry to `WORKLOG.md` including the commit hash/subject.
+12. Show the commit hash and final `git status --short`.

Also applies to: 45-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.opencode/commands/git-commit.md around lines 18 - 21, The WORKLOG.md step
requires a commit hash that doesn't exist yet (see step "8. Append a concise
entry to `WORKLOG.md`" and the template expecting a hash), so reorder the steps
so the commit is created before appending the WORKLOG entry (i.e., perform steps
10–11 to make the commit and capture the hash, then append the WORKLOG.md entry
including that hash, then stage/commit the WORKLOG if necessary), or
alternatively change the WORKLOG template to omit the commit hash pre-commit;
update the sequence around `WORKLOG.md`, the conventional commit instruction
example, and the post-commit status display to reflect the new ordering.

Comment thread entrypoint.sh
Comment on lines 54 to +58
OPENCODE_WORKSPACE="/home/opencode/workspace"
echo "> Set HOME to $OPENCODE_WORKSPACE (mounted workspace volume)"
export HOME="$OPENCODE_WORKSPACE"

exec gosu $APP_USER:$APP_GROUP "opencode"
exec gosu $APP_USER:$APP_GROUP opencode
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Export OPENCODE_WORKSPACE before launching OpenCode.

OPENCODE_WORKSPACE is assigned but not exported, so opencode won’t see it in its environment. That breaks the stated runtime contract for downstream command/skill behavior relying on this variable.

Suggested fix
 OPENCODE_WORKSPACE="/home/opencode/workspace"
 echo "> Set HOME to $OPENCODE_WORKSPACE (mounted workspace volume)"
+export OPENCODE_WORKSPACE
 export HOME="$OPENCODE_WORKSPACE"
 
 exec gosu $APP_USER:$APP_GROUP opencode

Based on learnings: “All projects must be located under /home/opencode/workspace with no exceptions…”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
OPENCODE_WORKSPACE="/home/opencode/workspace"
echo "> Set HOME to $OPENCODE_WORKSPACE (mounted workspace volume)"
export HOME="$OPENCODE_WORKSPACE"
exec gosu $APP_USER:$APP_GROUP "opencode"
exec gosu $APP_USER:$APP_GROUP opencode
OPENCODE_WORKSPACE="/home/opencode/workspace"
echo "> Set HOME to $OPENCODE_WORKSPACE (mounted workspace volume)"
export OPENCODE_WORKSPACE
export HOME="$OPENCODE_WORKSPACE"
exec gosu $APP_USER:$APP_GROUP opencode
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@entrypoint.sh` around lines 54 - 58, OPENCODE_WORKSPACE is only assigned and
not exported so the launched process (exec gosu $APP_USER:$APP_GROUP opencode)
won't receive it; export OPENCODE_WORKSPACE="/home/opencode/workspace" (before
exporting HOME and before the exec line) so the environment variable is
available to the opencode process and any downstream commands that depend on it.

Source: Learnings

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai can you please look at the feat/add-omp branch inside the entrypoint.sh and evaluate if that solution is better and more secure?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch entrypoint.sh from the feat/add-omp branch
gh api repos/jammsen/docker-opencode-sandbox/contents/entrypoint.sh?ref=feat/add-omp \
  --jq '.content' | base64 -d

Length of output: 3213



@jammsen, here's the comparison between feat/add-omp and the current PR branch for entrypoint.sh:


✅ What's better in feat/add-omp

Area Current PR (feat/cleanup-hook) feat/add-omp
gosu argument quoting gosu $APP_USER:$APP_GROUP opencode (unquoted) gosu "$APP_USER":"$APP_GROUP" "$TOOL" (properly quoted)
Tool argument quoting opencode (hardcoded, unquoted) "$TOOL" (variable, quoted)
Conditional HOME export Always sets HOME to workspace Only sets HOME to workspace for opencode; omp keeps its real HOME — architecturally cleaner

⚠️ Same issue remains in feat/add-omp

OPENCODE_WORKSPACE is still assigned but not exportedopencode (or omp) won't see it as an environment variable. The fix is the same as my original suggestion:

 OPENCODE_WORKSPACE="/home/opencode/workspace"
+export OPENCODE_WORKSPACE

🔴 New concern introduced in feat/add-omp

The interactive prompt has no timeout:

read -r -p "Enter selection [1]: " SELECTION

In headless/non-TTY Docker environments (e.g., docker run -d, CI pipelines, restart policies), this will hang indefinitely waiting for input. A timeout with a sensible default would make it safe:

read -r -t 10 -p "Enter selection [1]: " SELECTION || SELECTION="1"

Summary

feat/add-omp is better in quoting and the conditional HOME logic, but it shares the OPENCODE_WORKSPACE export gap and introduces a new hang risk with the unguarded read. If you plan to merge feat/add-omp eventually, both issues should be addressed there too.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai

Regarding - "OPENCODE_WORKSPACE is still assigned but not exported" - So what you are saying is we try to set a variable when its not existent or empty? Isnt this still in bash scope and not in shell scope? Can you give a full example of this, rundown workflow and a flow-diagram for this please?

Regarding - "In headless/non-TTY Docker environments (e.g., docker run -d, CI pipelines, restart policies), this will hang indefinitely waiting for input." - Yes thats right, please consult the README.md this repo is a "build it yourself locally with docker-compose sandbox, the sets for setup are litteraly git clone and run script. The script contains docker compose build and up. Thats by design: The cotainer has to wait for the interactive input in which agentic-coding tool to start, or if they dont, the containier cant start fully. Its what this "sandbox" is about, very hardenend Docker Environment out of the box, with a select picker that "shall not break!", Gandalf would say, to let the user work in it.

Thanks for the review!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jammsen, great questions — let me break both down.


1. Bash scope vs. exported environment variables

You're right that OPENCODE_WORKSPACE is accessible within the bash script itself. But the issue is about what child processes can see, not the bash script.

Here's the distinction:

Syntax Bash-local (current shell) Child process environment
VAR=value ✅ Visible Not inherited
export VAR=value ✅ Visible Inherited

When exec gosu $APP_USER:$APP_GROUP opencode runs, it replaces the current process with a new opencode process running as a different user. That new process starts with a fresh environment composed only of exported variables — bare bash-local assignments never make it across the exec boundary.

Concrete example

#!/usr/bin/env bash
NOT_EXPORTED="hello"
export EXPORTED="world"

bash -c 'echo "NOT_EXPORTED=$NOT_EXPORTED"'   # prints: NOT_EXPORTED=
bash -c 'echo "EXPORTED=$EXPORTED"'           # prints: EXPORTED=world

Flow diagram

flowchart TD
    A["entrypoint.sh\n(bash, PID 1, root)"] --> B["OPENCODE_WORKSPACE=...\nbash-local only ❌"]
    A --> C["export HOME=...\nenvironment variable ✅"]
    B & C --> D["exec gosu opencode:opencode opencode\nreplaces current process"]
    D --> E["opencode process\nnew PID, non-root user"]
    E --> F{"ENV: OPENCODE_WORKSPACE?"}
    E --> G{"ENV: HOME?"}
    F -->|"not exported → absent"| H["❌ opencode cannot see\nOPENCODE_WORKSPACE"]
    G -->|"was exported → present"| I["✅ opencode sees\nHOME=/home/opencode/workspace"]
Loading

So the fix is simply:

 OPENCODE_WORKSPACE="/home/opencode/workspace"
+export OPENCODE_WORKSPACE
 echo "> Set HOME to $OPENCODE_WORKSPACE (mounted workspace volume)"
 export HOME="$OPENCODE_WORKSPACE"

This ensures opencode — and any downstream commands/skills relying on $OPENCODE_WORKSPACE — can actually read it from their environment.


2. Interactive TTY / headless concern — acknowledged ✅

You're absolutely right — I missed the sandbox-first design intent. This is a local, interactive, git-clone-and-run setup where the user must be present at the terminal. The interactive read waiting for tool selection is intentional and correct by design. I withdraw that concern entirely.

Thanks for the thorough explanation! 🐇 🤔


✏️ Learnings added
Learnt from: jammsen
Repo: jammsen/docker-opencode-sandbox PR: 3
File: entrypoint.sh:54-58
Timestamp: 2026-06-06T13:58:50.595Z
Learning: In jammsen/docker-opencode-sandbox, the `feat/add-omp` branch introduces an interactive `read` prompt in `entrypoint.sh` (no timeout) for the user to select which agentic coding tool to launch (e.g., opencode or omp). This is intentional by design: the repo is a local sandbox (`docker-compose build && up`) that requires interactive terminal input at startup. The container is not intended for headless/CI/non-TTY use. Flagging the missing `read -t` timeout as an issue is incorrect for this repo.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix that, please re-review.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh api repos/jammsen/docker-opencode-sandbox/contents/entrypoint.sh?ref=feat/add-omp \
  --jq '.content' | base64 -d

Length of output: 3896



@jammsen, the duplicate case block is gone — great fix! Here's the full re-review:


✅ Complete security checklist — current feat/add-omp

# Finding Status
1 set -euo pipefail
2 umask 0027
3 readonly APP_USER APP_GROUP APP_HOME
4 Numeric PUID/PGID regex validation (^[1-9][0-9]*$)
5 readonly OPENCODE_WORKSPACE
6 export OPENCODE_WORKSPACE
7 Workspace dir existence check before launch
8 Full path + quoted gosu (/usr/local/sbin/gosu "$APP_USER":"$APP_GROUP" "$TOOL")
9 Single tightened case with explicit 1|"", 2, and * warning

🟡 One minor dead-code observation (low priority)

The zero-check block (lines 28–32) is now unreachable:

# This block can never be reached anymore:
if [[ "${PUID}" -eq 0 ]] || [[ "${PGID}" -eq 0 ]]; then
    ...
fi

The regex ^[1-9][0-9]*$ already guarantees PUID/PGID are positive integers — 0 fails the regex and exits before reaching this check. It's harmless dead code, but could be removed for clarity:

-if [[ "${PUID}" -eq 0 ]] || [[ "${PGID}" -eq 0 ]]; then
-    echo ">>> [Config] PUID=${PUID} PGID=${PGID} — Running the application user as root is not supported."
-    echo "    This container is designed to drop privileges after setup. Please set non-zero values for PUID and PGID."
-    exit 1
-fi

That's the only remaining cleanup item. Everything else is solid. 🐇 🔐


🧠 Learnings used
Learnt from: jammsen
Repo: jammsen/docker-opencode-sandbox PR: 3
File: entrypoint.sh:54-58
Timestamp: 2026-06-06T13:58:50.595Z
Learning: In jammsen/docker-opencode-sandbox, the `feat/add-omp` branch introduces an interactive `read` prompt in `entrypoint.sh` (no timeout) for the user to select which agentic coding tool to launch (e.g., opencode or omp). This is intentional by design: the repo is a local sandbox (`docker-compose build && up`) that requires interactive terminal input at startup. The container is not intended for headless/CI/non-TTY use. Flagging the missing `read -t` timeout as an issue is incorrect for this repo.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah good catch, i get it its basically what we have already but better, therefor its not needed anymore. What wondering already a bit but left it in, because your code reference placed it about this block hahaha

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the unreachble statement, please re-evaluate

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh api repos/jammsen/docker-opencode-sandbox/contents/entrypoint.sh?ref=feat/add-omp \
  --jq '.content' | base64 -d

Length of output: 3801



@jammsen, confirmed — the dead-code block is cleanly removed. Here's the final complete checklist:


✅ Final security checklist — feat/add-omp

# Finding Status
1 set -euo pipefail
2 umask 0027
3 readonly APP_USER APP_GROUP APP_HOME
4 Numeric PUID/PGID regex validation (^[1-9][0-9]*$)
5 Unreachable zero-check block removed
6 readonly OPENCODE_WORKSPACE
7 export OPENCODE_WORKSPACE
8 Workspace dir existence check before launch
9 Full path + quoted gosu
10 Single tightened case with 1|"", 2, explicit * warning

🟡 One final low-priority observation — set -u + unguarded env vars

Now that set -u is active, any variable not exported into the container environment will cause the script to abort with a cryptic unbound variable error before your nice error message fires. The three affected vars are PUID, PGID, and ENABLE_OMP:

-if ! [[ "${PUID}" =~ ^[1-9][0-9]*$ ]] || ! [[ "${PGID}" =~ ^[1-9][0-9]*$ ]]; then
+if ! [[ "${PUID:-}" =~ ^[1-9][0-9]*$ ]] || ! [[ "${PGID:-}" =~ ^[1-9][0-9]*$ ]]; then

-if [[ "$ENABLE_OMP" = "true" ]] && ...
+if [[ "${ENABLE_OMP:-}" = "true" ]] && ...

The :- provides an empty-string default when the variable is unset — the regex and string checks then fail gracefully with your own error messages rather than bash's generic one. If your Dockerfile/compose always sets these via ENV/environment:, you'll never hit this in practice, but it's good defensive hygiene.

Other than that — the script is solid. Great iterative work! 🐇 🔐


🧠 Learnings used
Learnt from: jammsen
Repo: jammsen/docker-opencode-sandbox PR: 3
File: entrypoint.sh:54-58
Timestamp: 2026-06-06T13:58:50.595Z
Learning: In jammsen/docker-opencode-sandbox, the `feat/add-omp` branch introduces an interactive `read` prompt in `entrypoint.sh` (no timeout) for the user to select which agentic coding tool to launch (e.g., opencode or omp). This is intentional by design: the repo is a local sandbox (`docker-compose build && up`) that requires interactive terminal input at startup. The container is not intended for headless/CI/non-TTY use. Flagging the missing `read -t` timeout as an issue is incorrect for this repo.

"agent": {
"build": {
"prompt": "You must maintain a file called WORKLOG.md in the current project working directory at all times.\\n\\nRules:\\n- Before starting any task, check if WORKLOG.md exists and read it to understand prior context.\\n- After completing EACH task, immediately update WORKLOG.md with: what was done (specific files changed, exact lines modified), what was found (exact issues, not vague summaries), and what still needs doing with current date and time in german timezone. Do NOT proceed to the next task until WORKLOG.md has been updated.\\n- If WORKLOG.md does not exist, create it before doing anything else.",
"prompt": "All projects are located under /home/opencode/workspace. Work only there, with no exceptions, unless the user explicitly asks for access outside the sandbox. Follow the sandbox rules from /home/opencode/.config/opencode/AGENTS.md. Before finishing any task that changes files, read WORKLOG.md in the active project directory if present and append a concise entry with the current Europe/Berlin timestamp from `TZ=Europe/Berlin date \"+%d.%m.%Y, %H:%M (%Z)\"`, changed files, concrete findings, and pending follow-ups.",
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Comment thread scripts/reset-sandbox.sh

while [[ $# -gt 0 ]]; do
case "$1" in
-y|--yes)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--destroy

would remove -y and --yes, its clear that reset destroy stuff, i think --destroy as a quick setting is more "authoritative" for what is going to happen.

Comment thread scripts/reset-sandbox.sh
$ROOT_DIR/workspace/.gitkeep
$ROOT_DIR/data/.gitkeep

This cannot be undone.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a destructive operation, this cannot be undone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants