mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-09 06:15:21 +02:00
codex/github-app-wizard-review
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
932c8bec56 |
fix(ci): bake the managed runtime identity into cloud images (#13210)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Managed deployments start from the image built by the Cloud workflow. > - The managed runtime requests user and group 1001. > - The image currently builds the node user as 1000. > - Startup must remap that user, which can walk a large mounted home directory. > - This pull request uses the existing Docker build arguments to bake user and group 1001 into Cloud images. > - Matching the runtime identity removes that startup work and helps avoid health-check retries. ## Linked Issues or Issue Description Refs #13208, #1923, and #7861. Searched open and closed PRs for the Cloud UID change. The older #7861 addresses build context and volume ownership repair. This change uses the existing identity arguments in the Cloud workflow and preserves ownership repair. **What happened?** A measured rollout had a container log `Updating node UID to 1001` after startup. The container stayed at this step for at least 2 minutes 55 seconds before rollback stopped it. The baked node identity was 1000, while the managed runtime requested 1001. A health check timed out and the target required a second deployment attempt. **Expected behavior** Cloud images should already have the managed runtime identity. A matching image should skip user and group remapping. Fresh or mismatched volumes must still receive ownership repair. **Steps to reproduce** 1. Build the current Cloud image with its default build arguments. 2. Start it with `USER_UID=1001`, `USER_GID=1001`, and a populated home volume. 3. Observe the startup user remap before the application starts. **Paperclip version or commit** `fc06f7f05f42c675be71ff0927b6334405d520ed` **Deployment mode** Docker on managed hosts. ## What Changed - Pass `USER_UID=1001` and `USER_GID=1001` to the Cloud image build. - Check the pushed digest's baked identity before the entrypoint can repair it. Then check the normal entrypoint's effective identity and writable home before publishing the verified full-SHA tag. - Add a workflow regression and two entrypoint cases for a matching Cloud identity, including a mismatched volume. - Document the runtime identity and the first-build cache cost. ## Verification - Focused workflow and artifact tests: 27 passed. - Entrypoint tests: 11 passed. Actionlint passed. Full local `pnpm -r typecheck` passed. Full local `pnpm build` passed. The manual [Cloud image build](https://github.com/paperclipai/paperclip/actions/runs/34575473213) passed on the exact PR head. It checked Sentry, baked and effective identity, writable home, orphan reaping, and full-SHA publication. The new identity check took one second. All 30 PR checks passed; the Storybook workflow was intentionally skipped. Greptile reviewed commit `114d408f637a0b53e2e2b1339c263779b1e4ae54` at 5/5 with no findings or open threads. - The full local suite for the same application source was already run in #13205. Its macOS general-server phase had 10,471 passes and 70 failures in seven unchanged files. Those failures included missing Runner fixtures, filesystem errors, timeouts, a port conflict, and a load-count mismatch. After configuring Cargo and rebuilding fixtures, 37 of 38 native tests passed; one unchanged native-resume assertion still failed. Linux PR CI passed. This change adds entrypoint tests and does not change application code. ## Risks - The first build must rebuild layers that depend on the base image identity. Later builds can reuse them. - A future managed runtime identity change must update these build arguments and checks together. - The Dockerfile's self-hosted defaults remain 1000. Runtime overrides and mounted-volume ownership repair remain supported. - The observed startup delay supports this change, but fleet timing also includes provider startup, image pull, canary order, and retries. No fixed end-to-end gain is claimed before a live rollout. ## Model Used OpenAI GPT-6 through Codex, with reasoning, code execution, and tool use. The exact serving model ID and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (focused workflow tests; full-suite limitations are listed above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
dd1a7f5290 |
Ensure app-home ownership before the privilege drop, not only on remap (#10530)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The Docker image persists all instance state (project checkouts, worktrees, run logs, uploads) under `PAPERCLIP_HOME`, and deployments mount a volume there for durability > - The entrypoint starts as root and drops privileges to the `node` user, but it fixes `PAPERCLIP_HOME` ownership only when it remaps the user's UID/GID > - A freshly mounted volume arrives root-owned and shadows the image's build-time `chown`, so a default-UID boot drops privileges onto an unwritable home and the server crashes on its first `mkdir` > - This pull request makes the entrypoint probe the home's ownership and chown whenever it does not match the runtime user, before the privilege drop > - The benefit is that the image works out of the box on any platform-managed volume, with the common already-correct boot staying chown-free ## Linked Issues or Issue Description No public issue exists — describing the bug inline (per the bug report template). **What happened?** Running the image with a freshly created volume mounted at `/paperclip` (a Docker named volume, a Kubernetes PV, or any platform-managed volume) and the default `USER_UID`/`USER_GID` crashes on boot: `Error: EACCES: permission denied, mkdir '/paperclip/instances/default/logs'`. **Expected behavior** The container boots and initializes its instance tree on the mounted volume, exactly as it does when `/paperclip` is the image's own (build-time chowned) directory. **Steps to reproduce** 1. `docker volume create paperclip-data` 2. `docker run -v paperclip-data:/paperclip ghcr.io/paperclipai/paperclip:<any current tag>` 3. Observe the EACCES crash on the first `mkdir` under `/paperclip`. **Root cause** `scripts/docker-entrypoint.sh` chowns `/paperclip` only inside its UID/GID remap branch (`changed=1`). A fresh volume mount is root-owned and shadows the image's build-time `chown node:node /paperclip`; with the default 1000:1000 no remap happens, so no chown happens, and `gosu node` drops onto an unwritable home. **Paperclip version or commit:** reproduces on `master` and any published image. **Deployment mode:** any; observed on managed-cloud volume mounts and reproducible with plain Docker named volumes. **Installation method:** Docker image (`ghcr.io/paperclipai/paperclip`). **Related PRs (dedup search):** no open or merged PR touches the entrypoint ownership logic; the entrypoint's privilege-handling tests were added previously and this extends them. No duplicate found. ## What Changed - `scripts/docker-entrypoint.sh`: the remap-conditional `chown` is replaced by an ownership probe — after any UID/GID remap, the entrypoint stats `PAPERCLIP_HOME` (default `/paperclip`) and runs `chown -R node:node` only when the owner does not match the runtime user, before `exec gosu node`. Covers fresh root-owned mounts and trees written under a previous UID mapping; the already-correct boot performs no chown. The unprivileged (non-root start) branch is unchanged. - `server/src/__tests__/docker-entrypoint.test.ts`: `stat` stub added to the harness; new cases for the fresh root-owned mount with default UID/GID and for `PAPERCLIP_HOME`-relative probing; the remap case now models the post-remap ownership mismatch. ## Verification - `pnpm vitest run server/src/__tests__/docker-entrypoint.test.ts` — 7 passed (5 existing behaviors unchanged, 2 new). - Live on a managed deployment: a container that crash-looped with the EACCES above boots cleanly once the home is chowned before the drop (the same effect this entrypoint change produces; forced there by a UID remap as an interim workaround). ## Risks - Low. Behavior changes only for boots where `PAPERCLIP_HOME` exists with mismatched ownership — exactly the boots that crash today. `chown -R` on a large previously-mismatched tree adds one-time boot latency; correctly-owned homes skip it entirely. Kubernetes restricted / OpenShift non-root starts keep the existing exec-directly path untouched. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic; Claude Code CLI with extended thinking and tool use; tests executed locally via Vitest). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above (no duplicates; extends the existing entrypoint privilege tests) - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge |
||
|
|
c21f70ef1c |
fix: skip gosu when already running as target user (#2908)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - The reference container image must be deployable on both Docker Compose (where it starts as root and `gosu`'s a `USER_UID`/`USER_GID` switch) and Kubernetes (where the pod is typically constrained by PodSecurity) > - The Kubernetes operator (paperclipinc/paperclip-operator#45) sets `runAsNonRoot: true`, `runAsUser: 1000`, `allowPrivilegeEscalation: false`, and `drop: ALL` capabilities by default — the unconditional `usermod` + `gosu` flow in the entrypoint requires root + `CAP_SETUID` / `CAP_SETGID`, making the image undeployable on any cluster enforcing baseline or restricted PodSecurity > - Without root, neither the user remap nor `gosu` can ever succeed — so the fix is a runtime branch: non-root starts `exec` the command directly (warning if the runtime UID/GID differs from the requested one), while root starts keep the existing `usermod`+`gosu` flow > - This also covers platforms that assign arbitrary UIDs (OpenShift restricted SCC), which previously crashed with a cryptic `usermod: Permission denied` > - The benefit is one image that works for both deployment shapes with no operator-side workaround — pure superset, no breaking change ## Linked Issues or Issue Description Refs paperclipinc/paperclip-operator#45 (cross-repo) — the operator's default pod security context (`runAsNonRoot: true`, `runAsUser: 1000`, `allowPrivilegeEscalation: false`, `drop: ALL`) is blocked by this entrypoint behavior. The operator shipped a stopgap (paperclipinc/paperclip-operator#46 lets the CRD override the security context); this PR is the image-side fix that makes the secure defaults work out of the box. Supersedes #2904 (v1 of this branch). No in-repo issue covers this directly — problem described in-PR following the bug-report template: **What happened** The entrypoint unconditionally runs `usermod`/`groupmod`/`chown` + `exec gosu node`, which requires root plus `CAP_SETUID` / `CAP_SETGID` — any non-root start crashes (`gosu` cannot drop privileges; a mismatched UID dies earlier at `usermod: Permission denied`), making the reference image undeployable on clusters enforcing baseline or restricted PodSecurity. **Expected behavior** A non-root container `exec`s the command directly (with a clear warning if its UID/GID differs from the requested `USER_UID`/`USER_GID`, since a remap is impossible without root). The existing root + `usermod` + `gosu` flow is preserved for Docker Compose, where the container starts as root and switches to the requested UID/GID. **Deployment mode** Kubernetes with baseline/restricted PodSecurity and OpenShift-style arbitrary-UID platforms (failing cases); Docker Compose root-start (must keep working). ## What Changed - **`scripts/docker-entrypoint.sh`** — branch on the runtime UID: - **Non-root start** → `exec "$@"` directly. If the runtime UID/GID differs from `USER_UID`/`USER_GID`, print a one-line warning to stderr first (the remap is impossible without root; the warning keeps volume-permission mismatches diagnosable instead of failing cryptically inside `usermod`). - **Root start** → unchanged: `usermod`/`groupmod` remap when requested, `chown` of `/paperclip` when a remap happened, then `exec gosu node "$@"`. ## Verification **Automated:** `server/src/__tests__/docker-entrypoint.test.ts` runs the real entrypoint with `id`/`usermod`/`groupmod`/`chown`/`gosu` stubbed via PATH and asserts all five privilege branches (root+defaults, root+remap, non-root match, arbitrary non-root UID, GID mismatch) — runs in the regular server suite, no Docker needed. **Manual (Docker):** ran the entrypoint on `node:lts-trixie-slim` (the image's actual base) across the full matrix, with `gosu` stubbed to a marker: - [x] Root start, defaults → no remap, `gosu node` invoked (Docker Compose flow unchanged) - [x] Root start, `USER_UID=1001`/`USER_GID=1001` → `Updating node UID/GID to 1001` + `gosu node` (remap flow unchanged) - [x] Non-root `--user 1000:1000` (the operator's `runAsUser: 1000` shape) → silent direct exec, command runs as 1000:1000 - [x] Non-root `--user 1234:1234` (arbitrary UID) → warning `running unprivileged as 1234:1234; cannot remap to requested 1000:1000`, then direct exec (previously: crash) - [x] Non-root `--user 1000:1001` (GID mismatch) → warning, then direct exec - [x] Baseline check: master's entrypoint fails for any non-root start (gosu/usermod require root) End-to-end cluster verification under restricted PodSecurity exercises the same branch as the `--user 1000:1000` case above; the operator repo's deploy is the natural place for that smoke test once this ships in an image tag. ## Risks - **Backward-compatible.** Docker Compose / root-entrypoint path is byte-for-byte the same flow — `usermod`/`gosu` runs whenever the container starts as root. - **Behavior change only for previously-broken starts.** Non-root containers used to crash; they now run. The only observable difference for a *working* deployment is none. - **Mismatched non-root UID/GID warns instead of failing.** Deliberate: the remap is impossible without root, and arbitrary-UID platforms (OpenShift) rely on group-writable volumes; a hard fail would keep them broken. The stderr warning preserves diagnosability. - **No new env vars, no API surface.** Pure entrypoint behavior change gated on the runtime UID. - **Restricted PodSecurity ready.** The non-root branch needs no Linux capabilities — works under `drop: ALL`. ## Model Used Claude Opus 4.6; rebase, non-root generalization, and verification matrix by Claude Fable 5 (1M context). ## Checklist - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] Thinking path traces from project context to this change - [x] Model used specified - [x] Checked ROADMAP.md — not in conflict with planned core work (agent-runtime sandbox images use `tini`, no gosu — unaffected) - [x] Tests run locally and pass (new `docker-entrypoint.test.ts` covering all five privilege branches, plus a manual Docker matrix on the real base image; see Verification) - [x] No UI changes - [x] Documented risks above - [x] Will address all Greptile and reviewer comments before merge Unblocks the default (non-overridden) security context of paperclipinc/paperclip-operator#45 / #46. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |