release: clide v1–v3 — security hardening, Codex support, observability docs#49
Merged
itscooleric merged 13 commits intomainfrom Mar 8, 2026
Merged
release: clide v1–v3 — security hardening, Codex support, observability docs#49itscooleric merged 13 commits intomainfrom
itscooleric merged 13 commits intomainfrom
Conversation
…CK (#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 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: sync main into dev (CI workflow fix)
* 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>
…ix (#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>
… 24.04 built-in ubuntu user Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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: 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 - 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>
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.
Summary
Merges the
devintegration branch intomain, shipping all work from the v1 (Developer Experience), v2 (Security Hardening), and v3 (Documentation) milestones.What's included
gh auth setup-gitin entrypoint, Dockerfile HEALTHCHECKclaude-entrypoint.sh+ newcodexcompose service./clide codexcommandSECURITY.md), operational runbook (RUNBOOK.md), compatibility matrixdevbranchMilestone status after this merge
Test plan
main— no Dockerfile errorsdocker compose up clide— container starts healthy (HEALTHCHECK passes within 30s)docker compose up clidewithout credentials → container errors at startup (ttyd auth required by default)docker compose up clidewithTTYD_USER/TTYD_PASSset → web terminal accessible with authdocker compose up codex→ Codex CLI available in container (requiresOPENAI_API_KEY)./clide codex -- --help→ passes args through correctlydocker inspect <container> --format '{{.State.Health.Status}}'→healthy🤖 Generated with Claude Code