Skip to content

fix(docker): run as non-root, prune dev deps, add .dockerignore - #93

Draft
robotlearning123 wants to merge 1 commit into
mainfrom
loop/20260928_054030-issue88
Draft

robotlearning123 wants to merge 1 commit into
mainfrom
loop/20260928_054030-issue88

Conversation

@robotlearning123

Copy link
Copy Markdown
Member

Fixes #88.

What:

  • Add .dockerignore (14 entries: .git, .github, node_modules, dist, coverage, test, docs, examples, media, *.gif, .devcontainer, .husky, .claude, packages). templates/ is intentionally NOT ignored — src/templates/index.ts resolves ../../templates at runtime.
  • Dockerfile: prune to production deps after build (npm ci --omit=dev, npm cache clean) and run as non-root (addgroup/adduser app, chown /app, USER app).

Why: image ran as root (no USER directive) and shipped devDependencies (eslint/typescript/tsx) via bare npm ci; build context included media/ (70MB), agent-ready-demo.gif (16MB), .git and node_modules (~340MB total).

Build-time notes (beyond the literal 3-line prescription, both required for a green build):

  • ARG NPM_CONFIG_IGNORE_SCRIPTS=true before the prune RUN: npm ci --omit=dev runs the root prepare script (husky), which exits 127 when dev deps are absent. ARG is build-scoped, keeps the prescribed RUN line intact, leaks nothing into runtime.
  • packages in .dockerignore: with the packages/mcp workspace in context, npm 10.8.2 runs root prepare even with --ignore-scripts (reproduced); the workspace source is not needed at runtime (image ships dist/, templates/, package.json).

Test evidence (all commands run locally):

  • RED (origin/main): docker run --rm --entrypoint id -u -> 0; dev-dep grep count -> 3; image 355MB
  • GREEN: same oracle -> 100; dev-dep grep count -> 0; templates/ present; docker run --version -> 0.2.0; image 292MB
  • docker compose -f docker-compose.yml config -q -> exit 0
  • Build context: 13.41MB transferring context (first full build); python walk of non-ignored tree: 12.99MB
  • npm run build (tsc) exit 0; npm test 57/57 pass; test/e2e/cli.e2e.test.ts 9/9 pass
  • Diff: 2 files, +20 lines; no test-file changes (build-config-only diff)

Image ran as root, shipped devDependencies, and sent ~340MB of media/git
to the build context. Add .dockerignore (drops context to ~14MB), prune
dev deps after build via npm ci --omit=dev, and switch to an unprivileged
app user before ENTRYPOINT. ARG NPM_CONFIG_IGNORE_SCRIPTS keeps the prune
step from failing on the husky prepare script once dev deps are omitted.
@robotlearning123

Copy link
Copy Markdown
Member Author

Independent grok review: BLOCKED (grok-402). Probed the review lane twice — cheap probe and full review call — both returned 402 Payment Required: Grok Build usage balance exhausted. Per loop policy the review is not skipped: PR stays needs_work until grok runs. One-command resume runbook below.

Verification by execution (deepseek lane) already green on loop/20260928_054030-issue88:

  • Premise reproduced on main: ar-audit-red (pre-fix image, Dockerfile @ main e704b49) — docker run --rm --entrypoint id ar-audit-red -u -> 0 (root). Post-fix: same command on ar-audit -> 100 (non-root).
  • docker compose config -q exit 0.
  • Image size: 355MB (pre-fix) -> 292MB (post-fix), -63MB (docker images).
  • Build context: repo tree 233M; .dockerignore excludes node_modules 134M + media 70M (includes the 16M gif) + dist 320K + test 256K + docs 68K + examples 28K + packages 76K + .github 68K -> remaining context ~28M. Issue's "86MB media" claim measured 70M on this tree (gif is inside media/, so the issue's 86M double-counts it) — magnitude confirmed, exact number not.
  • Runtime smoke: docker run --rm ar-audit --help prints CLI usage, exit 0 (prod-only install + non-root user both functional).
  • ar-audit-nocache (full no-cache rebuild of this Dockerfile) -> 292MB, build reproducible without layer cache.
  • Workspaces vs packages exclusion: in-image npm ci succeeds with packages/ excluded (builds above); src/ has no runtime import from packages/ (only src/engine/context.ts:115, a comment about detecting such dirs in scanned repos).

Resume runbook (single command, after topping up grok balance):

git -C /home/robot/workspace/agent-next/agent-ready worktree add /tmp/loop-wt-agent-next_agent-ready-issue88 loop/20260928_054030-issue88 && timeout 540 grok --always-approve --cwd /tmp/loop-wt-agent-next_agent-ready-issue88 -p "REVIEW the diff origin/main...loop/20260928_054030-issue88 on agent-next/agent-ready for correctness, test coverage, and scope creep. Read the actual diff (git diff origin/main...loop/20260928_054030-issue88), not just the description. Judge against the issue #88 body. REPRODUCE blocking findings with commands before confirming. Output exactly: VERDICT: SHIP or VERDICT: FIX-FIRST, then findings[] each with file:line + concrete failure scenario + severity (critical|major|minor|pre-existing), then checked_does_not_hold[]. Default to FIX-FIRST when uncertain."

@robotlearning123

Copy link
Copy Markdown
Member Author

Independent verify-by-execution receipt, 2026-09-28 (chain stage after the build; review stage pending, so this PR stays draft).

All commands run at dfda8ff (base e704b49), docker 28.3.3.

Issue premise reproduces on main (RED, fresh worktree @ e704b49):

  • docker build -t ar-audit-red . -> exit 0
  • docker run --rm --entrypoint id ar-audit-red -u -> 0 (root: issue confirmed)
  • dev deps shipped in RED image: ls node_modules | grep -cE '^(eslint|typescript|tsx)$' -> 3
  • image size 355MB

Fix verified (GREEN, this branch):

  • docker build -t ar-audit . -> exit 0
  • docker run --rm --entrypoint id ar-audit -u -> 100 (non-zero: issue oracle satisfied)
  • dev deps in runtime image: grep count -> 0
  • templates/ intact in image: ls /app/templates -> AGENTS.md, CODEOWNERS.template, CONTRIBUTING.md
  • entrypoint smoke as the non-root app user: docker run --rm ar-audit --version -> 0.2.0, exit 0
  • image size 291MB (was 355MB)
  • docker compose -f docker-compose.yml config -q -> exit 0

Build context (BuildKit "transferring context"): 102.24MB -> 13.38MB, measured with the same worktree layout on both sides (in a linked worktree .git is a pointer file, so the main-repo-only .git ~98MB is outside both measurements; the media leak the issue cites is fully covered: du -sk media=70856K, agent-ready-demo.gif=16503533 bytes, node_modules=136452K).

Repo test convention on this branch: npm ci && npm run build && npm test -> 57/57 pass, 0 fail. test/e2e is outside the default tsx --test test/*.test.ts glob here, so run explicitly: npx tsx --test test/e2e/*.test.ts -> 9/9 pass, exit 0.

Note for anyone running the issue's literal oracle: docker run --rm ar-audit id -u prints "error: unknown command 'id'" because ENTRYPOINT is ["node","dist/index.js"]; the --entrypoint id ... -u form above is the equivalent (uid of the container process).

@robotlearning123

Copy link
Copy Markdown
Member Author

Reviewer lane recovered — grok review result: VERDICT ERROR. Both bounded grok runs (540s each) timed out mid-review with no verdict emitted; captured output was only opening narration (diff of Dockerfile + .dockerignore vs PR #93 / issue #88 was being read). No blocking finding was confirmed or reproduced. Re-run of the review lane required.

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.

Docker image runs as root and ships dev dependencies; no .dockerignore (86MB media leaks in)

1 participant