Repository navigation
Add .dockerignore to exclude secrets and non-essential files from build context - #24
Merged
Merged
Conversation
…text Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add .dockerignore to prevent leaking secrets
Add .dockerignore to exclude secrets and non-essential files from build context
Mar 5, 2026
itscooleric
marked this pull request as ready for review
March 5, 2026 04:32
10 tasks
itscooleric
added a commit
that referenced
this pull request
Apr 23, 2026
* Initial plan
* feat: add PR checks GitHub Actions workflow
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
* fix: pin Node.js to 22.x and ttyd to 1.7.7, fix hadolint issues
- setup_lts.x → setup_22.x (prevents surprise LTS major jumps)
- ttyd /latest/ → pinned 1.7.7 (current stable)
- Replace wget with curl throughout (DL4001)
- Add --no-install-recommends to all apt-get install (DL3015)
- Add SHELL pipefail before pipe commands (DL4006)
- Add .hadolint.yaml ignoring DL3008/DL3016 (npm/apt tools intentionally unpinned)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve CI failures in hadolint, compose-validate, markdown-lint
- Dockerfile: quote $(uname -m) via ARCH variable to fix SC2046
- Workflow: add config: .hadolint.yaml so hadolint picks up ignore rules
- Workflow: cp .env.example .env before compose-validate to provide env file
- Add .markdownlint.yaml: disable MD013/022/031/032 (style-only rules)
- DEPLOY.md: remove stray closing fence with no matching open
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: resolve remaining CI failures (hadolint ignore, MD034, MD040)
- Workflow: add ignore: DL3008 DL3016 to hadolint step (config file
path wasn't being resolved correctly inside the action container)
- README.md: add language specifier to env code fences (MD040)
- .markdownlint.yaml: disable MD034 (bare URLs in technical docs)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: use inline hadolint ignore directives before each RUN instruction
The hadolint-action ignore input and .hadolint.yaml config file were not
being applied. Inline directives are the most reliable approach.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add claude-entrypoint.sh to skip first-run prompts, enforce CLAUDE_CODE_SIMPLE=1
Adds a dedicated entrypoint for the claude service that pre-seeds
~/.claude.json (onboarding flags + workspace trust) so the container
starts directly into the CLI without interactive setup prompts.
CLAUDE_CODE_SIMPLE=1 is now set consistently via docker-compose.yml
(default), Makefile, and the clide wrapper script. README and DEPLOY
docs updated with startup behaviour notes and a reset troubleshooting
step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove unused CLAUDE_CONFIG var (SC2034), extend entrypoint to shell service
- Drop the unused `CLAUDE_CONFIG` bash variable from claude-entrypoint.sh
to fix shellcheck SC2034 warning
- Change `exec claude "$@"` to `exec "${@:-claude}"` so the entrypoint
is reusable: no args → runs claude (existing behaviour); args → runs
those instead (e.g. /bin/bash for the shell service)
- Wire the shell service in docker-compose.yml to use claude-entrypoint.sh
with /bin/bash as command, so claude config is pre-seeded before the
interactive shell starts and `claude` works without prompts from make shell
- Update README to reflect that shell also benefits from pre-seeding
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* [#16] Update .env.example and README with git identity env vars (#17)
* docs: add git identity vars to .env.example and README
Adds GIT_AUTHOR_* / GIT_COMMITTER_* placeholders to .env.example so
git authorship persists correctly across container runs without needing
git config --global on each session.
Also updates README setup step to reference cp .env.example .env
as the starting point rather than manual inline snippets.
Closes #16
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add OAuth token support for Claude Code, consolidate auth + identity docs
- claude-entrypoint.sh: detect CLAUDE_CODE_OAUTH_TOKEN, skip primaryApiKey
injection when OAuth is set, log which auth method is active, unset
ANTHROPIC_API_KEY from env when OAuth takes priority
- docker-compose.yml: pass CLAUDE_CODE_OAUTH_TOKEN through to all services
- .env.example: replace single API key comment with structured OAuth/API key
section; restore git identity vars block
- README.md: update prerequisites and CLIs table for dual auth; add
Claude Code authentication section; consolidate setup step 1 to show
OAuth, API key, and git identity vars together
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): Claude auth + tmux multi-pane for web terminal (#22)
* fix(web): pre-seed Claude auth in web terminal + add tmux multi-pane support
- entrypoint.sh now calls claude-entrypoint.sh at container start so
CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY from .env are wired into
.claude.json before any ttyd bash session runs `claude` (fixes #21)
- Mirrors the ANTHROPIC_API_KEY env cleanup from claude-entrypoint.sh
so web terminal env matches `make shell` exactly
- Install tmux in Dockerfile; ship a .tmux.conf with mouse support,
sane splits (| and -), 256-colour, and status bar (closes #18)
- Web terminal (ttyd) now launches into `tmux new-session -A -s main`
so browser refreshes re-attach to the existing session
- Shell service gets opt-in tmux via CLIDE_TMUX=1 in .env, leaving
existing single-pane behaviour unchanged by default
- Expose CLIDE_TMUX in docker-compose base env block
- README: document tmux shortcuts and CLIDE_TMUX opt-in
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(entrypoint): suppress CLIDE_TMUX during claude-entrypoint pre-seed call, add sync comment
- Pass CLIDE_TMUX= (blank) when calling claude-entrypoint.sh from entrypoint.sh
so the tmux opt-in branch is skipped — the web terminal manages its own tmux
session via ttyd and doesn't need a transient session spun up during pre-seeding
- Add "keep in sync" comment on the duplicated env cleanup block to make it obvious
it mirrors the logic in claude-entrypoint.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve CI failures (shellcheck SC1007, markdownlint MD036)
- entrypoint.sh: quote empty string in inline env assignment (CLIDE_TMUX='')
- README.md: replace bold paragraphs with #### headings for Option 1/2
to satisfy MD036 (no-emphasis-as-heading)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add .dockerignore to exclude secrets and non-essential files from build context (#24)
* Security: run container as non-root user (#23)
* Initial plan
* Security: run container as non-root user (clide, UID 1000)
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
* fix: harden HOME handling in claude entrypoint (#26)
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
Co-authored-by: eric is cool <e@ericiscool.net>
* Add configurable iptables egress firewall for container network isolation (#25)
* Initial plan
* Add configurable iptables egress firewall (CLIDE_FIREWALL / CLIDE_ALLOWED_HOSTS)
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
* feat: add Python 3 dev tooling (pytest + ruff) to container (#32)
Installs python3/python3-venv via apt and creates a shared venv at
/opt/pyenv with pytest and ruff pre-installed on PATH. Lets Claude
Code run Python test suites and lint/format code without any manual
setup inside the container.
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK (#33)
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK
- (#4) Flip ttyd auth logic: credentials now required by default; container
exits with a clear error if neither creds nor TTYD_NO_AUTH=true are set.
Added TTYD_NO_AUTH=true as the explicit opt-out for unauthenticated access.
- (#14) Add `gh auth setup-git` to entrypoint so git push/fetch works
seamlessly via GH_TOKEN without embedding tokens in remote URLs.
- (#10) Add HEALTHCHECK to Dockerfile — curls ttyd every 30s with a 10s
startup grace period and 3 retries before marking the container unhealthy.
- Updated .env.example to document new ttyd auth defaults and TTYD_NO_AUTH.
Closes #4, #10, #14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address Copilot review feedback on PR #33
- gh auth setup-git is now best-effort: skips gracefully if GH_TOKEN is
absent or gh is unauthenticated, so the web terminal can still start
without GitHub credentials.
- Fix TTYD auth precedence: credentials now take priority over
TTYD_NO_AUTH=true; setting both is treated as a hard config error
rather than silently disabling auth.
- HEALTHCHECK now respects TTYD_BASE_PATH so it doesn't produce false
unhealthy status when a non-root base path is configured.
- HEALTHCHECK validates TTYD_PORT is numeric before use to prevent
shell injection via environment variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: gosu privilege drop for firewall + compose hardening (#34)
* fix: gosu privilege drop for firewall, compose hardening
Fixes the silent firewall no-op (#30) caused by iptables requiring
geteuid()==0 even with CAP_NET_ADMIN when the container runs as the
non-root clide user.
- (#30) Install gosu; set Dockerfile back to USER root so entrypoints
start as root. All entrypoints (firewall.sh, claude-entrypoint.sh,
entrypoint.sh) now apply any root-only setup first (iptables, config
writes) then exec gosu clide to drop privileges before the workload.
The firewall is now always effective when NET_ADMIN is present.
- (#30) Hardcode HOME_DIR=/home/clide in claude-entrypoint.sh to avoid
resolving to /root when running as root; chown .claude.json to clide
after writing.
- (#30) gosu clide gh auth setup-git in entrypoint.sh so gh config
writes to clide's home, not root's.
- (#6) Add cap_drop: ALL, security_opt: no-new-privileges:true, and
resource guardrails (mem_limit: 4g, cpus: 4.0, pids_limit: 512) to
the docker-compose.yml base anchor. cap_add: NET_ADMIN is preserved
for the firewall.
Closes #30, #6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: suppress DL3002 for intentional USER root before gosu drop
The last USER is root by design — entrypoints apply iptables rules as
root then exec gosu clide to drop privileges before the workload starts.
Suppress hadolint DL3002 with a pragma rather than papering over the
intentional pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: run PR checks on PRs targeting dev (#38)
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK (#33)
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK
- (#4) Flip ttyd auth logic: credentials now required by default; container
exits with a clear error if neither creds nor TTYD_NO_AUTH=true are set.
Added TTYD_NO_AUTH=true as the explicit opt-out for unauthenticated access.
- (#14) Add `gh auth setup-git` to entrypoint so git push/fetch works
seamlessly via GH_TOKEN without embedding tokens in remote URLs.
- (#10) Add HEALTHCHECK to Dockerfile — curls ttyd every 30s with a 10s
startup grace period and 3 retries before marking the container unhealthy.
- Updated .env.example to document new ttyd auth defaults and TTYD_NO_AUTH.
Closes #4, #10, #14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address Copilot review feedback on PR #33
- gh auth setup-git is now best-effort: skips gracefully if GH_TOKEN is
absent or gh is unauthenticated, so the web terminal can still start
without GitHub credentials.
- Fix TTYD auth precedence: credentials now take priority over
TTYD_NO_AUTH=true; setting both is treated as a hard config error
rather than silently disabling auth.
- HEALTHCHECK now respects TTYD_BASE_PATH so it doesn't produce false
unhealthy status when a non-root base path is configured.
- HEALTHCHECK validates TTYD_PORT is numeric before use to prevent
shell injection via environment variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: gosu privilege drop for firewall + compose hardening (#34)
* fix: gosu privilege drop for firewall, compose hardening
Fixes the silent firewall no-op (#30) caused by iptables requiring
geteuid()==0 even with CAP_NET_ADMIN when the container runs as the
non-root clide user.
- (#30) Install gosu; set Dockerfile back to USER root so entrypoints
start as root. All entrypoints (firewall.sh, claude-entrypoint.sh,
entrypoint.sh) now apply any root-only setup first (iptables, config
writes) then exec gosu clide to drop privileges before the workload.
The firewall is now always effective when NET_ADMIN is present.
- (#30) Hardcode HOME_DIR=/home/clide in claude-entrypoint.sh to avoid
resolving to /root when running as root; chown .claude.json to clide
after writing.
- (#30) gosu clide gh auth setup-git in entrypoint.sh so gh config
writes to clide's home, not root's.
- (#6) Add cap_drop: ALL, security_opt: no-new-privileges:true, and
resource guardrails (mem_limit: 4g, cpus: 4.0, pids_limit: 512) to
the docker-compose.yml base anchor. cap_add: NET_ADMIN is preserved
for the firewall.
Closes #30, #6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: suppress DL3002 for intentional USER root before gosu drop
The last USER is root by design — entrypoints apply iptables rules as
root then exec gosu clide to drop privileges before the workload starts.
Suppress hadolint DL3002 with a pragma rather than papering over the
intentional pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: run PR checks on PRs targeting dev branch
Adds dev to the pull_request branch filter so CI runs on feature
branches merging into dev, not just main.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add Codex CLI (OpenAI) support (#35)
* Initial plan
* feat: add Codex CLI (OpenAI) support
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
* fix: forward args to codex wrapper command (#36)
* fix: suppress DL3059 for consecutive npm RUN instructions
hadolint flags consecutive RUN instructions; suppressed with a pragma
since consolidating the npm installs would hurt layer caching.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: trigger checks after rebase
* chore: run PR checks on PRs targeting dev (#38)
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK (#33)
* fix: ttyd auth default-on, gh credential helper, Dockerfile HEALTHCHECK
- (#4) Flip ttyd auth logic: credentials now required by default; container
exits with a clear error if neither creds nor TTYD_NO_AUTH=true are set.
Added TTYD_NO_AUTH=true as the explicit opt-out for unauthenticated access.
- (#14) Add `gh auth setup-git` to entrypoint so git push/fetch works
seamlessly via GH_TOKEN without embedding tokens in remote URLs.
- (#10) Add HEALTHCHECK to Dockerfile — curls ttyd every 30s with a 10s
startup grace period and 3 retries before marking the container unhealthy.
- Updated .env.example to document new ttyd auth defaults and TTYD_NO_AUTH.
Closes #4, #10, #14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address Copilot review feedback on PR #33
- gh auth setup-git is now best-effort: skips gracefully if GH_TOKEN is
absent or gh is unauthenticated, so the web terminal can still start
without GitHub credentials.
- Fix TTYD auth precedence: credentials now take priority over
TTYD_NO_AUTH=true; setting both is treated as a hard config error
rather than silently disabling auth.
- HEALTHCHECK now respects TTYD_BASE_PATH so it doesn't produce false
unhealthy status when a non-root base path is configured.
- HEALTHCHECK validates TTYD_PORT is numeric before use to prevent
shell injection via environment variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: gosu privilege drop for firewall + compose hardening (#34)
* fix: gosu privilege drop for firewall, compose hardening
Fixes the silent firewall no-op (#30) caused by iptables requiring
geteuid()==0 even with CAP_NET_ADMIN when the container runs as the
non-root clide user.
- (#30) Install gosu; set Dockerfile back to USER root so entrypoints
start as root. All entrypoints (firewall.sh, claude-entrypoint.sh,
entrypoint.sh) now apply any root-only setup first (iptables, config
writes) then exec gosu clide to drop privileges before the workload.
The firewall is now always effective when NET_ADMIN is present.
- (#30) Hardcode HOME_DIR=/home/clide in claude-entrypoint.sh to avoid
resolving to /root when running as root; chown .claude.json to clide
after writing.
- (#30) gosu clide gh auth setup-git in entrypoint.sh so gh config
writes to clide's home, not root's.
- (#6) Add cap_drop: ALL, security_opt: no-new-privileges:true, and
resource guardrails (mem_limit: 4g, cpus: 4.0, pids_limit: 512) to
the docker-compose.yml base anchor. cap_add: NET_ADMIN is preserved
for the firewall.
Closes #30, #6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: suppress DL3002 for intentional USER root before gosu drop
The last USER is root by design — entrypoints apply iptables rules as
root then exec gosu clide to drop privileges before the workload starts.
Suppress hadolint DL3002 with a pragma rather than papering over the
intentional pattern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: run PR checks on PRs targeting dev branch
Adds dev to the pull_request branch filter so CI runs on feature
branches merging into dev, not just main.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: re-trigger checks after workflow update
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
Co-authored-by: eric is cool <e@ericiscool.net>
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: architecture diagram, threat model, runbook, compatibility matrix (#37)
* docs: architecture diagram, threat model, runbook, compatibility matrix
- (#12) Add Mermaid architecture diagram to README showing host/container
trust boundary, services, and allowlisted internet endpoints.
- (#8) Add SECURITY.md — trust boundaries, attack surface breakdown,
threat scenario table, and deployment hardening recommendations.
- (#11) Add RUNBOOK.md — health checks, start/stop, logs, rebuild
procedures, credential rotation, firewall and web terminal
troubleshooting, Docker health state reference.
- (#9) Add compatibility matrix to README covering host OS, Docker
version, CPU architecture, browser support, and firewall requirements.
- Link SECURITY.md and RUNBOOK.md from README "Additional docs" table.
Closes #8, #9, #11, #12
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: re-trigger checks after workflow update
* fix: resolve markdown-lint failures in docs PR
- Rename duplicate 'Architecture' heading to 'CPU architecture' (MD024)
- Rename duplicate 'Egress firewall' heading to 'Egress firewall support' (MD024)
- Add 'text' language to ASCII art fenced block in SECURITY.md (MD040)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: change clide UID from 1000 to 1001 to avoid conflict with Ubuntu 24.04 built-in ubuntu user
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove redundant mkdir/chown for /home/clide in entrypoint
useradd -m in the Dockerfile already creates /home/clide with correct
ownership. The chown was also failing at runtime due to cap_drop: ALL
removing the CHOWN capability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: run claude config node script as clide user via gosu
cap_drop: ALL removes CAP_DAC_OVERRIDE, meaning root inside the container
is subject to normal file permission checks and cannot write to /home/clide.
Running the node script via gosu clide ensures it writes the config as the
correct user.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add SETUID and SETGID capabilities required by gosu
cap_drop: ALL removes CAP_SETUID and CAP_SETGID, which gosu needs to
switch from root to the clide user in entrypoint scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add missing GitHub hosts to firewall allowlist
git clone/fetch/pull serve pack objects via objects.githubusercontent.com,
raw file access goes through raw.githubusercontent.com, and gh release
uploads use uploads.github.com — all previously blocked by the egress filter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add egress reject audit logging (LOG before REJECT in firewall.sh)
Closes #40. Adds rate-limited LOG target (10/min, burst 5) before REJECT rules for both IPv4 and IPv6. Logs appear under CLIDE-REJECT: prefix in dmesg/kern.log.
* fix: writable pyenv for agent + git safe.directory pre-configured (#55)
* fix: make pyenv writable by clide + pre-configure git safe.directory
Two quality-of-life improvements for agent dev sessions:
**1. Writable Python venv (/opt/pyenv)**
Transfer /opt/pyenv ownership to the clide user so the agent can
`pip install -r /workspace/<repo>/requirements.txt` on demand without a
container rebuild. Previously the venv was root-owned, causing permission
errors when installing workspace project dependencies (e.g. clem's PyYAML,
Flask, pydantic). pytest + ruff remain pre-installed as a baseline; all other
deps are installed per-session as needed.
**2. git safe.directory = * (eliminate per-session config noise)**
Volume-mounted workspace repos are often owned by the host UID, which
differs from clide (UID 1000). Git refuses to operate on these repos unless
`safe.directory` is configured, causing wasted tokens and boilerplate at the
start of every session. Setting `safe.directory = *` once at build time in
the clide user's gitconfig eliminates this entirely. This is appropriate for
a single-user dev sandbox.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: README — clide-owned venv, pip install pattern, git safe.directory, codex in diagram (#56)
* Initial plan
* docs: update README for clide-owned venv, pip install pattern, git safe.directory section
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
* feat: add persistent session memory across container restarts (#57)
* feat: add persistent session memory across container restarts
- Add named volume (claude-data) for ~/.claude so auto memory, settings,
and project learnings survive container restarts
- Seed CLAUDE.md from a template on first run if none exists in /workspace
- Template search order: project .claude/CLAUDE.md.template → bundled default
- Bundle a sensible default CLAUDE.md.template in the image
https://claude.ai/code/session_012jgpaUaGUgmGyoH9edLbyg
* fix: disable MD060 lint rule and remove trailing blank line in README
The markdownlint-cli2 action upgraded to markdownlint v0.40.0 which
introduced MD060 (table-column-style). Disable it to match existing
compact table style, and fix one MD012 violation (double blank line).
https://claude.ai/code/session_012jgpaUaGUgmGyoH9edLbyg
---------
Co-authored-by: Claude <noreply@anthropic.com>
* docs: replace mermaid diagram with ASCII art in README
Mermaid doesn't render in all contexts (terminal, raw markdown viewers,
email). ASCII box-drawing diagram shows the same architecture and works
everywhere.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add CLI-style ASCII banner to README
Block-letter logo with architecture summary — shows the trust model,
egress allowlist, and key selling points at a glance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add glab (GitLab CLI) to container (closes #62)
- Dockerfile: install glab v1.47.0 binary (pinned, single deb install)
- docker-compose.yml: pass GITLAB_TOKEN + GITLAB_HOST env vars through
- entrypoint.sh + claude-entrypoint.sh: wire glab config on startup,
best-effort — missing token prints a notice but never blocks startup
- .env.example: document GITLAB_TOKEN + GITLAB_HOST with group token guidance
- README: add glab to Included CLIs table
Group access token is the recommended auth method — scoped to one group,
not tied to a personal account, survives credential rotation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add language tag to ASCII banner code block (MD040)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: replace named volume with workspace-local .clide/ directory
Move Claude session storage from the `claude-data` named Docker volume
(`/home/clide/.claude`) into `/workspace/.clide/` on the bind-mounted
project directory. This eliminates the root-ownership permission issues
that Docker named volumes cause and keeps all clide state in one place.
The entrypoint now:
- Creates /workspace/.clide/ with correct clide ownership
- Symlinks ~/.claude → /workspace/.clide/
- Migrates existing data from old named-volume mounts automatically
https://claude.ai/code/session_01CvrggoLEZAcjJaoQFggHs9
* fix: prevent chown failure from crashing entrypoint on bind mounts
The container drops all capabilities and only re-adds a few. Without the
CHOWN/FOWNER capabilities, `chown clide:clide /workspace/.clide` fails
with "Operation not permitted", and `set -euo pipefail` causes the
entire entrypoint to abort.
- Add CHOWN and FOWNER to cap_add in docker-compose.yml
- Make all chown calls in claude-entrypoint.sh non-fatal with chmod 777
fallback, so the script degrades gracefully on filesystems that don't
support ownership changes (NFS, FUSE, rootless Docker)
https://claude.ai/code/session_01JrufWPKGMLtHPmCks8kj9A
* fix: add DAC_OVERRIDE cap and configurable UID/GID for bind mount permissions
cap_drop: ALL removes DAC_OVERRIDE, preventing root from creating
/workspace/.clide on bind mounts owned by the host user. Adding it back
lets the entrypoint set up persistent storage regardless of host UID.
Also makes clide's UID/GID configurable via CLIDE_UID/CLIDE_GID build
args (default 1000) so the container user matches the host user for
normal file operations. Closes #53.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add -l flag to useradd to satisfy hadolint DL3046
Prevents excessively large images when high UIDs are used.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pin all dependency versions in Dockerfile
Pin npm and pip packages to specific versions for reproducible builds:
- @anthropic-ai/claude-code@2.1.71
- @openai/codex@0.112.0
- pytest==9.0.2
- ruff==0.15.5
All versions use build ARGs (npm) or inline pins (pip) so they're easy
to bump. Removes the DL3016 hadolint ignore now that npm packages are
pinned.
Closes #7
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: create ~/.claude symlink as clide user instead of root
The entrypoint runs as root but /home/clide is owned by clide. Without
DAC_OVERRIDE (or on older cached images), root can't create symlinks
there. Use gosu clide for all symlink operations in clide's home dir.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: document all CLIDE_* environment variables in README
Add a comprehensive reference table covering CLIDE_UID, CLIDE_GID,
CLIDE_FIREWALL, CLIDE_ALLOWED_HOSTS, CLIDE_TMUX, and CLIDE_FIREWALL_DONE
with defaults, where each is set, and descriptions.
Closes #7
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: warn about clide-web-1 restart destroying in-memory state
Add a prominent warning to the RUNBOOK explaining that /workspace
persists (bind mount) but tmux sessions, running processes, shell
history, and temp files are lost on container restart.
Closes #11
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: document Docker socket non-exposure and CI guard
Add a dedicated section to SECURITY.md explaining that the Docker
socket is intentionally not mounted, why this matters for preventing
container escape, and providing a sample CI guard to enforce this.
Closes #14
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* bootstrap: LICENSE + scrub internal refs for public release
- Added MIT LICENSE
- Replaced wubi.sh domain refs with example.com placeholders
- Replaced Bernard references with generic 'production host'
Tracking: forge/eric#12, career#27
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* clide#15: README rewrite — research/data framing
* fix: add language specifier to bare fenced code blocks (MD040)
---------
Co-authored-by: itscooleric <itscooleric@users.noreply.github.com>
Co-authored-by: itscooleric <25490177+itscooleric@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Without a
.dockerignore, everydocker buildsends the full repo context to the daemon — including.env(which holds API keys and PATs) and other files the image never uses.Changes
.dockerignorewith exclusions grouped by purpose:.env,.env.*.git,.gitignore.vscode,.markdownlint.yaml,.hadolint.yaml.githubREADME.md,DEPLOY.mddocker-compose*.ymlnode_modulesThe Dockerfile only
COPYsentrypoint.sh,claude-entrypoint.sh, and.tmux.conf— nothing excluded here is needed in the image.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.