Yuki Takei 1 месяц назад
Родитель
Сommit
b63b5f0bc3

+ 199 - 199
.kiro/specs/official-docker-image/design.md

@@ -2,48 +2,48 @@
 
 ## Overview
 
-**Purpose**: GROWI 公式 Docker イメージの Dockerfile と entrypoint を 2025-2026 年のベストプラクティスに基づきモダナイズし、セキュリティ強化・メモリ管理最適化・ビルド効率向上を実現する。
+**Purpose**: Modernize the Dockerfile and entrypoint for the GROWI official Docker image based on 2025-2026 best practices, achieving enhanced security, optimized memory management, and improved build efficiency.
 
-**Users**: インフラ管理者(ビルド・デプロイ)、GROWI 運用者(メモリチューニング)、Docker image エンドユーザー(docker-compose での利用)が対象。
+**Users**: Infrastructure administrators (build/deploy), GROWI operators (memory tuning), and Docker image end users (usage via docker-compose).
 
-**Impact**: 既存の 3 ステージ Dockerfile を 5 ステージ構成に再設計。ベースイメージを Docker Hardened Images (DHI) に移行。entrypoint を shell script から TypeScript に変更し(Node.js 24 のネイティブ TypeScript 実行)、シェル不要の完全ハードニング構成を実現。
+**Impact**: Redesign the existing 3-stage Dockerfile into a 5-stage configuration. Migrate the base image to Docker Hardened Images (DHI). Change the entrypoint from a shell script to TypeScript (using Node.js 24 native TypeScript execution), achieving a fully hardened configuration that requires no shell.
 
 ### Goals
 
-- DHI ベースイメージ採用による CVE 最大 95% 削減
-- **シェル完全不要の TypeScript entrypoint** — Node.js 24 のネイティブ TypeScript 実行(type stripping)、DHI runtime の攻撃面最小化をそのまま維持
-- `GROWI_HEAP_SIZE` / cgroup 自動算出 / V8 デフォルトの 3 段フォールバックによるメモリ管理
-- `turbo prune --docker` パターンによるビルドキャッシュ効率向上
-- gosu → `process.setuid/setgid`(Node.js ネイティブ)による権限ドロップ
+- Up to 95% CVE reduction through DHI base image adoption
+- **Fully shell-free TypeScript entrypoint** — Node.js 24 native TypeScript execution (type stripping), maintaining the minimized attack surface of the DHI runtime as-is
+- Memory management via 3-tier fallback: `GROWI_HEAP_SIZE` / cgroup auto-calculation / V8 default
+- Improved build cache efficiency through the `turbo prune --docker` pattern
+- Privilege drop via gosu → `process.setuid/setgid` (Node.js native)
 
 ### Non-Goals
 
-- Kubernetes マニフェスト / Helm chart の変更(GROWI.cloud 側の `GROWI_HEAP_SIZE` 設定は対象外)
-- アプリケーションコードの変更(gc() 追加、.pipe() 移行等は別 spec)
-- docker-compose.yml の更新(ドキュメント更新のみ)
-- Node.js 24 未満のバージョンサポート
-- HEALTHCHECK 命令の追加(k8s は独自 probe を使用、Docker Compose ユーザーは自前で設定可能)
+- Changes to Kubernetes manifests / Helm charts (GROWI.cloud `GROWI_HEAP_SIZE` configuration is out of scope)
+- Application code changes (adding gc(), migrating to .pipe(), etc. are separate specs)
+- Updating docker-compose.yml (documentation updates only)
+- Support for Node.js versions below 24
+- Adding HEALTHCHECK instructions (k8s uses its own probes, Docker Compose users can configure their own)
 
 ## Architecture
 
 ### Existing Architecture Analysis
 
-**現行 Dockerfile の 3 ステージ構成:**
+**Current Dockerfile 3-stage configuration:**
 
-| Stage | Base Image | 役割 |
+| Stage | Base Image | Role |
 |-------|-----------|------|
-| `base` | `node:20-slim` | pnpm + turbo のインストール |
+| `base` | `node:20-slim` | Install pnpm + turbo |
 | `builder` | `base` | `COPY . .` → install → build → artifacts |
-| release (unnamed) | `node:20-slim` | gosu install → artifacts 展開 → 実行 |
+| release (unnamed) | `node:20-slim` | gosu install → artifact extraction → execution |
 
-**主な課題:**
-- `COPY . .` でモノレポ全体がビルドレイヤーに含まれる
-- pnpm バージョンがハードコード (`PNPM_VERSION="10.4.1"`)
-- `---frozen-lockfile` の typo
-- ベースイメージが node:20-slim(CVE が蓄積しやすい)
-- メモリ管理フラグなし
-- OCI ラベルなし
-- gosu のインストールに apt-get が必要(runtime に apt 依存)
+**Main issues:**
+- `COPY . .` includes the entire monorepo in the build layer
+- pnpm version is hardcoded (`PNPM_VERSION="10.4.1"`)
+- Typo in `---frozen-lockfile`
+- Base image is node:20-slim (prone to CVE accumulation)
+- No memory management flags
+- No OCI labels
+- gosu installation requires apt-get (runtime dependency on apt)
 
 ### Architecture Pattern & Boundary Map
 
@@ -75,27 +75,27 @@ graph TB
 **Architecture Integration:**
 - Selected pattern: Multi-stage build with dependency caching separation
 - Domain boundaries: Build concerns (stages 1-4) vs Runtime concerns (stage 5 + entrypoint)
-- Existing patterns preserved: pnpm deploy による本番依存抽出、tar.gz アーティファクト転送
-- New components: pruner ステージ(turbo prune)、TypeScript entrypoint
-- **Key change**: gosu + shell script → TypeScript entrypoint(`process.setuid/setgid` + `fs` module + `child_process.execFileSync/spawn`)。busybox/bash のコピーが不要になり、DHI runtime の攻撃面最小化をそのまま維持。Node.js 24 の type stripping で `.ts` を直接実行
-- Steering compliance: Debian ベース維持(glibc パフォーマンス)、モノレポビルドパターン維持
+- Existing patterns preserved: Production dependency extraction via pnpm deploy, tar.gz artifact transfer
+- New components: pruner stage (turbo prune), TypeScript entrypoint
+- **Key change**: gosu + shell script → TypeScript entrypoint (`process.setuid/setgid` + `fs` module + `child_process.execFileSync/spawn`). Eliminates the need for copying busybox/bash, maintaining the minimized attack surface of the DHI runtime as-is. Executes `.ts` directly via Node.js 24 type stripping
+- Steering compliance: Maintains Debian base (glibc performance), maintains monorepo build pattern
 
 ### Technology Stack
 
 | Layer | Choice / Version | Role in Feature | Notes |
 |-------|------------------|-----------------|-------|
-| Base Image (build) | `dhi.io/node:24-debian13-dev` | ビルドステージのベース | apt/bash/git/util-linux 利用可能 |
-| Base Image (runtime) | `dhi.io/node:24-debian13` | リリースステージのベース | 極小構成、CVE 95% 削減、**シェルなし** |
-| Entrypoint | Node.js (TypeScript) | 初期化・ヒープ算出・権限ドロップ・プロセス起動 | Node.js 24 native type stripping、busybox/bash 不要 |
-| Privilege Drop | `process.setuid/setgid` (Node.js) | root → node ユーザー切替 | 外部バイナリ不要 |
-| Build Tool | `turbo prune --docker` | モノレポ最小化 | Turborepo 公式推奨 |
-| Package Manager | pnpm (wget standalone) | 依存管理 | corepack 不採用(Node.js 25+ で廃止予定) |
+| Base Image (build) | `dhi.io/node:24-debian13-dev` | Base for build stages | apt/bash/git/util-linux available |
+| Base Image (runtime) | `dhi.io/node:24-debian13` | Base for release stage | Minimal configuration, 95% CVE reduction, **no shell** |
+| Entrypoint | Node.js (TypeScript) | Initialization, heap calculation, privilege drop, process startup | Node.js 24 native type stripping, no busybox/bash needed |
+| Privilege Drop | `process.setuid/setgid` (Node.js) | root → node user switch | No external binaries needed |
+| Build Tool | `turbo prune --docker` | Monorepo minimization | Official Turborepo recommendation |
+| Package Manager | pnpm (wget standalone) | Dependency management | corepack not adopted (scheduled for removal in Node.js 25+) |
 
-> TypeScript entrypoint 採用の経緯、busybox-static/setpriv との比較は `research.md` を参照。
+> For the rationale behind adopting the TypeScript entrypoint and comparison with busybox-static/setpriv, see `research.md`.
 
 ## System Flows
 
-### Entrypoint 実行フロー
+### Entrypoint Execution Flow
 
 ```mermaid
 flowchart TD
@@ -116,13 +116,13 @@ flowchart TD
 ```
 
 **Key Decisions:**
-- cgroup v2 (`/sys/fs/cgroup/memory.max`) を優先、v1 にフォールバック
-- cgroup v1 の unlimited 値(巨大な数値)はフラグなしとして扱う(閾値: 64GB)
-- `--max-heap-size` は entrypoint プロセスではなく、spawn される子プロセス(アプリ本体)に渡される
-- migration は `child_process.execFileSync` で直接 node を呼び出す(`npm run` 不使用、シェル不要)
-- アプリ起動は `child_process.spawn` + シグナルフォワーディングで PID 1 の責務を果たす
+- Prioritize cgroup v2 (`/sys/fs/cgroup/memory.max`), fall back to v1
+- Treat cgroup v1 unlimited value (very large number) as no flag (threshold: 64GB)
+- `--max-heap-size` is passed to the spawned child process (the application itself), not the entrypoint process
+- Migration is invoked directly via `child_process.execFileSync` calling node (no `npm run`, no shell needed)
+- App startup uses `child_process.spawn` + signal forwarding to fulfill PID 1 responsibilities
 
-### Docker Build フロー
+### Docker Build Flow
 
 ```mermaid
 flowchart LR
@@ -160,53 +160,53 @@ flowchart LR
 
 | Requirement | Summary | Components | Interfaces | Flows |
 |-------------|---------|------------|------------|-------|
-| 1.1 | DHI ベースイメージ | base, release ステージ | — | Build フロー |
-| 1.2 | syntax ディレクティブ更新 | Dockerfile ヘッダ | — | — |
-| 1.3 | pnpm wget インストール維持 | base ステージ | — | Build フロー |
-| 1.4 | frozen-lockfile typo 修正 | deps ステージ | — | — |
-| 1.5 | pnpm バージョン非ハードコード | base ステージ | — | — |
-| 2.1 | GROWI_HEAP_SIZE | docker-entrypoint.ts | 環境変数 I/F | Entrypoint フロー |
-| 2.2 | cgroup 自動算出 | docker-entrypoint.ts | cgroup fs I/F | Entrypoint フロー |
-| 2.3 | フラグなしフォールバック | docker-entrypoint.ts | — | Entrypoint フロー |
-| 2.4 | GROWI_OPTIMIZE_MEMORY | docker-entrypoint.ts | 環境変数 I/F | Entrypoint フロー |
-| 2.5 | GROWI_LITE_MODE | docker-entrypoint.ts | 環境変数 I/F | Entrypoint フロー |
-| 2.6 | --max-heap-size 使用 | docker-entrypoint.ts | spawn args | Entrypoint フロー |
-| 2.7 | NODE_OPTIONS 不使用 | docker-entrypoint.ts | — | Entrypoint フロー |
-| 3.1 | COPY . . 廃止 | pruner + deps ステージ | — | Build フロー |
-| 3.2 | pnpm cache mount 維持 | deps, builder ステージ | — | Build フロー |
-| 3.3 | apt cache mount 維持 | base ステージ | — | Build フロー |
-| 3.4 | .next/cache 除外 | builder ステージ | — | — |
-| 3.5 | bind from=builder パターン | release ステージ | — | Build フロー |
-| 4.1 | 非 root 実行 | docker-entrypoint.ts | process.setuid/setgid | Entrypoint フロー |
-| 4.2 | 不要パッケージ排除 | release ステージ | — | — |
-| 4.3 | .dockerignore 強化 | Dockerfile.dockerignore | — | — |
-| 4.4 | --no-install-recommends | base ステージ | — | — |
-| 4.5 | ビルドツール排除 | release ステージ | — | — |
-| 5.1 | OCI ラベル | release ステージ | — | — |
-| 5.2 | EXPOSE 維持 | release ステージ | — | — |
-| 5.3 | VOLUME 維持 | release ステージ | — | — |
-| 6.1 | ヒープサイズ算出ロジック | docker-entrypoint.ts | — | Entrypoint フロー |
-| 6.2 | 権限ドロップ exec | docker-entrypoint.ts | process.setuid/setgid | Entrypoint フロー |
-| 6.3 | /data/uploads 維持 | docker-entrypoint.ts | fs module | Entrypoint フロー |
-| 6.4 | /tmp/page-bulk-export 維持 | docker-entrypoint.ts | fs module | Entrypoint フロー |
-| 6.5 | CMD migrate 維持 | docker-entrypoint.ts | execFileSync | Entrypoint フロー |
-| 6.6 | --expose_gc 維持 | docker-entrypoint.ts | spawn args | Entrypoint フロー |
-| 6.7 | フラグログ出力 | docker-entrypoint.ts | console.log | Entrypoint フロー |
-| 6.8 | TypeScript で記述 | docker-entrypoint.ts | Node.js type stripping | — |
-| 7.1-7.5 | 後方互換性 | 全コンポーネント | — | — |
-| 8.1 | docker-new → docker 置換 | ディレクトリ構造 | ファイルシステム | — |
-| 8.2 | Dockerfile パス参照更新 | Dockerfile | — | — |
-| 8.3 | DHI レジストリログイン | buildspec.yml | secrets-manager | Build フロー |
-| 8.4 | buildspec Dockerfile パス確認 | buildspec.yml | — | Build フロー |
+| 1.1 | DHI base image | base, release stages | — | Build flow |
+| 1.2 | Update syntax directive | Dockerfile header | — | — |
+| 1.3 | Maintain pnpm wget installation | base stage | — | Build flow |
+| 1.4 | Fix frozen-lockfile typo | deps stage | — | — |
+| 1.5 | Non-hardcoded pnpm version | base stage | — | — |
+| 2.1 | GROWI_HEAP_SIZE | docker-entrypoint.ts | Environment variable I/F | Entrypoint flow |
+| 2.2 | cgroup auto-calculation | docker-entrypoint.ts | cgroup fs I/F | Entrypoint flow |
+| 2.3 | No-flag fallback | docker-entrypoint.ts | — | Entrypoint flow |
+| 2.4 | GROWI_OPTIMIZE_MEMORY | docker-entrypoint.ts | Environment variable I/F | Entrypoint flow |
+| 2.5 | GROWI_LITE_MODE | docker-entrypoint.ts | Environment variable I/F | Entrypoint flow |
+| 2.6 | Use --max-heap-size | docker-entrypoint.ts | spawn args | Entrypoint flow |
+| 2.7 | Do not use NODE_OPTIONS | docker-entrypoint.ts | — | Entrypoint flow |
+| 3.1 | Eliminate COPY . . | pruner + deps stages | — | Build flow |
+| 3.2 | Maintain pnpm cache mount | deps, builder stages | — | Build flow |
+| 3.3 | Maintain apt cache mount | base stage | — | Build flow |
+| 3.4 | Exclude .next/cache | builder stage | — | — |
+| 3.5 | bind from=builder pattern | release stage | — | Build flow |
+| 4.1 | Non-root execution | docker-entrypoint.ts | process.setuid/setgid | Entrypoint flow |
+| 4.2 | Exclude unnecessary packages | release stage | — | — |
+| 4.3 | Enhanced .dockerignore | Dockerfile.dockerignore | — | — |
+| 4.4 | --no-install-recommends | base stage | — | — |
+| 4.5 | Exclude build tools | release stage | — | — |
+| 5.1 | OCI labels | release stage | — | — |
+| 5.2 | Maintain EXPOSE | release stage | — | — |
+| 5.3 | Maintain VOLUME | release stage | — | — |
+| 6.1 | Heap size calculation logic | docker-entrypoint.ts | — | Entrypoint flow |
+| 6.2 | Privilege drop exec | docker-entrypoint.ts | process.setuid/setgid | Entrypoint flow |
+| 6.3 | Maintain /data/uploads | docker-entrypoint.ts | fs module | Entrypoint flow |
+| 6.4 | Maintain /tmp/page-bulk-export | docker-entrypoint.ts | fs module | Entrypoint flow |
+| 6.5 | Maintain CMD migrate | docker-entrypoint.ts | execFileSync | Entrypoint flow |
+| 6.6 | Maintain --expose_gc | docker-entrypoint.ts | spawn args | Entrypoint flow |
+| 6.7 | Flag log output | docker-entrypoint.ts | console.log | Entrypoint flow |
+| 6.8 | Written in TypeScript | docker-entrypoint.ts | Node.js type stripping | — |
+| 7.1-7.5 | Backward compatibility | All components | — | — |
+| 8.1 | Replace docker-new → docker | Directory structure | Filesystem | — |
+| 8.2 | Update Dockerfile path references | Dockerfile | — | — |
+| 8.3 | DHI registry login | buildspec.yml | secrets-manager | Build flow |
+| 8.4 | Verify buildspec Dockerfile path | buildspec.yml | — | Build flow |
 
 ## Components and Interfaces
 
 | Component | Domain/Layer | Intent | Req Coverage | Key Dependencies | Contracts |
 |-----------|-------------|--------|-------------|-----------------|-----------|
-| Dockerfile | Infrastructure | Docker イメージビルド定義 | 1.1-1.5, 3.1-3.5, 4.1-4.5, 5.1-5.3, 6.5, 8.2 | DHI images (P0), turbo (P0), pnpm (P0) | — |
-| docker-entrypoint.ts | Infrastructure | コンテナ起動時の初期化(TypeScript) | 2.1-2.7, 6.1-6.4, 6.6-6.8 | Node.js fs/child_process (P0), cgroup fs (P1) | Batch |
-| Dockerfile.dockerignore | Infrastructure | ビルドコンテキストフィルタ | 4.3 | — | — |
-| buildspec.yml | CI/CD | CodeBuild ビルド定義 | 8.3, 8.4 | AWS Secrets Manager (P0), dhi.io (P0) | Batch |
+| Dockerfile | Infrastructure | Docker image build definition | 1.1-1.5, 3.1-3.5, 4.1-4.5, 5.1-5.3, 6.5, 8.2 | DHI images (P0), turbo (P0), pnpm (P0) | — |
+| docker-entrypoint.ts | Infrastructure | Container startup initialization (TypeScript) | 2.1-2.7, 6.1-6.4, 6.6-6.8 | Node.js fs/child_process (P0), cgroup fs (P1) | Batch |
+| Dockerfile.dockerignore | Infrastructure | Build context filter | 4.3 | — | — |
+| buildspec.yml | CI/CD | CodeBuild build definition | 8.3, 8.4 | AWS Secrets Manager (P0), dhi.io (P0) | Batch |
 
 ### Infrastructure Layer
 
@@ -214,20 +214,20 @@ flowchart LR
 
 | Field | Detail |
 |-------|--------|
-| Intent | 5 ステージの Docker イメージビルド定義 |
+| Intent | 5-stage Docker image build definition |
 | Requirements | 1.1-1.5, 3.1-3.5, 4.1-4.5, 5.1-5.3, 6.5, 7.1-7.5 |
 
 **Responsibilities & Constraints**
-- 5 ステージ構成: `base` → `pruner` → `deps` → `builder` → `release`
-- DHI ベースイメージの使用(`dhi.io/node:24-debian13-dev` / `dhi.io/node:24-debian13`)
-- **runtime にシェル・追加バイナリのコピーなし**(Node.js entrypoint で全て完結)
-- OCI ラベルの付与
+- 5-stage configuration: `base` → `pruner` → `deps` → `builder` → `release`
+- Use of DHI base images (`dhi.io/node:24-debian13-dev` / `dhi.io/node:24-debian13`)
+- **No shell or additional binary copying in runtime** (everything is handled by the Node.js entrypoint)
+- OCI label assignment
 
 **Dependencies**
-- External: `dhi.io/node:24-debian13-dev` — ビルドベースイメージ (P0)
-- External: `dhi.io/node:24-debian13` — ランタイムベースイメージ (P0)
-- Outbound: pnpm — 依存管理 (P0)
-- Outbound: turbo — ビルドオーケストレーション (P0)
+- External: `dhi.io/node:24-debian13-dev` — Build base image (P0)
+- External: `dhi.io/node:24-debian13` — Runtime base image (P0)
+- Outbound: pnpm — Dependency management (P0)
+- Outbound: turbo — Build orchestration (P0)
 
 **Contracts**: Batch [x]
 
@@ -237,93 +237,93 @@ flowchart LR
 ```
 FROM dhi.io/node:24-debian13-dev AS base
 ```
-- apt-get で `ca-certificates`, `wget` をインストール(ビルド専用)
-- wget スタンドアロンスクリプトで pnpm をインストール(バージョンはスクリプトのデフォルト)
+- Install `ca-certificates`, `wget` via apt-get (build-only)
+- Install pnpm via wget standalone script (version uses script default)
 - pnpm add turbo --global
 
 **Stage 2: `pruner`**
 ```
 FROM base AS pruner
 ```
-- `COPY . .` でモノレポ全体をコピー
-- `turbo prune @growi/app --docker` で Docker 最適化ファイルを生成
-- 出力: `out/json/`(package.json 群)、`out/pnpm-lock.yaml`、`out/full/`(ソース)
+- `COPY . .` to copy the entire monorepo
+- `turbo prune @growi/app --docker` to generate Docker-optimized files
+- Output: `out/json/` (package.json files), `out/pnpm-lock.yaml`, `out/full/` (source)
 
 **Stage 3: `deps`**
 ```
 FROM base AS deps
 ```
-- `COPY --from=pruner` で json/ と lockfile のみコピー(キャッシュ効率化)
-- `pnpm install --frozen-lockfile` で依存インストール
-- `pnpm add node-gyp --global`(native modules 用)
+- `COPY --from=pruner` to copy only json/ and lockfile (for cache efficiency)
+- `pnpm install --frozen-lockfile` for dependency installation
+- `pnpm add node-gyp --global` (for native modules)
 
 **Stage 4: `builder`**
 ```
 FROM deps AS builder
 ```
-- `COPY --from=pruner` で full/ ソースをコピー
+- `COPY --from=pruner` to copy full/ source
 - `turbo run build --filter @growi/app`
 - `pnpm deploy out --prod --filter @growi/app`
-- artifacts を tar.gz にパッケージング(現行の内容を維持、`apps/app/tmp` 含む)
+- Package artifacts into tar.gz (maintaining current contents, including `apps/app/tmp`)
 
 **Stage 5: `release`**
 ```
 FROM dhi.io/node:24-debian13 AS release
 ```
-- **追加バイナリのコピーなし**(シェル・gosu・setpriv・busybox 一切不要)
-- artifacts を `--mount=type=bind,from=builder` で展開
-- `docker-entrypoint.ts` を COPY
-- OCI ラベル、EXPOSE、VOLUME を設定
+- **No additional binary copying** (no shell, gosu, setpriv, or busybox needed at all)
+- Extract artifacts via `--mount=type=bind,from=builder`
+- COPY `docker-entrypoint.ts`
+- Set OCI labels, EXPOSE, VOLUME
 - `ENTRYPOINT ["node", "/docker-entrypoint.ts"]`
 
 **Implementation Notes**
-- `turbo prune --docker` が pnpm workspace と互換でない場合のフォールバック: 最適化 COPY パターン(lockfile + package.json 群を先にコピー → install → ソースコピー → build)
-- DHI イメージの pull には `docker login dhi.io` が必要(CI/CD での認証設定が必要)
-- release ステージに apt-get は一切不要(現行の gosu install が完全に排除される)
+- Fallback if `turbo prune --docker` is incompatible with pnpm workspace: optimized COPY pattern (copy lockfile + package.json files first → install → copy source → build)
+- Pulling DHI images requires `docker login dhi.io` (authentication setup needed in CI/CD)
+- No apt-get is needed at all in the release stage (the current gosu installation is completely eliminated)
 
 #### docker-entrypoint.ts
 
 | Field | Detail |
 |-------|--------|
-| Intent | コンテナ起動時の初期化処理(ディレクトリ設定、ヒープサイズ算出、権限ドロップ、migration 実行、アプリ起動)。TypeScript で記述、Node.js 24 のネイティブ type stripping で直接実行 |
+| Intent | Container startup initialization processing (directory setup, heap size calculation, privilege drop, migration execution, app startup). Written in TypeScript, executed directly via Node.js 24 native type stripping |
 | Requirements | 2.1-2.7, 6.1-6.8 |
 
 **Responsibilities & Constraints**
-- **TypeScript で記述**: Node.js 24 のネイティブ type stripping で直接実行(`node docker-entrypoint.ts`)。enum は使用不可(erasable syntax のみ使用)
-- root 権限での初期化処理(`fs.mkdirSync`、`fs.symlinkSync`、`fs.chownSync` で実装)
-- 3 段フォールバックによるヒープサイズ決定(`fs.readFileSync` で cgroup 読み取り)
-- Node.js ネイティブの `process.setgid()` + `process.setuid()` で権限ドロップ
-- `child_process.execFileSync` で migration を直接実行(npm run 不使用、シェル不要)
-- `child_process.spawn` でアプリプロセスを起動し、SIGTERM/SIGINT をフォワード
-- **外部バイナリ依存なし**(Node.js の標準ライブラリのみ使用)
+- **Written in TypeScript**: Executed directly via Node.js 24 native type stripping (`node docker-entrypoint.ts`). Enums cannot be used (only erasable syntax is allowed)
+- Root privilege initialization processing (implemented with `fs.mkdirSync`, `fs.symlinkSync`, `fs.chownSync`)
+- Heap size determination via 3-tier fallback (cgroup reading via `fs.readFileSync`)
+- Privilege drop via Node.js native `process.setgid()` + `process.setuid()`
+- Direct migration execution via `child_process.execFileSync` (no npm run, no shell needed)
+- App process startup via `child_process.spawn` with SIGTERM/SIGINT forwarding
+- **No external binary dependencies** (uses only Node.js standard library)
 
 **Dependencies**
-- External: Node.js `fs` module — ファイルシステム操作 (P0)
-- External: Node.js `child_process` module — プロセス起動 (P0)
-- External: cgroup filesystem — メモリリミット取得 (P1)
+- External: Node.js `fs` module — Filesystem operations (P0)
+- External: Node.js `child_process` module — Process startup (P0)
+- External: cgroup filesystem — Memory limit retrieval (P1)
 - Inbound: Environment variables — GROWI_HEAP_SIZE, GROWI_OPTIMIZE_MEMORY, GROWI_LITE_MODE
 
 **Contracts**: Batch [x]
 
 ##### Batch / Job Contract
 
-- **Trigger**: コンテナ起動時(`ENTRYPOINT ["node", "/docker-entrypoint.ts"]` として実行)
+- **Trigger**: On container startup (executed as `ENTRYPOINT ["node", "/docker-entrypoint.ts"]`)
 - **Input / validation**:
-  - `GROWI_HEAP_SIZE`: 正の整数(MB 単位)。空文字列は未設定として扱う
-  - `GROWI_OPTIMIZE_MEMORY`: `"true"` のみ有効。それ以外は無視
-  - `GROWI_LITE_MODE`: `"true"` のみ有効。それ以外は無視
-  - cgroup v2: `/sys/fs/cgroup/memory.max` — 数値または `"max"`(unlimited)
-  - cgroup v1: `/sys/fs/cgroup/memory/memory.limit_in_bytes` — 数値(unlimited 時は巨大値)
-- **Output / destination**: `child_process.spawn` の引数として node フラグを直接渡す
-- **Idempotency & recovery**: コンテナ再起動時に毎回実行。冪等(`fs.mkdirSync` の `recursive: true` で安全)
+  - `GROWI_HEAP_SIZE`: Positive integer (in MB). Empty string is treated as unset
+  - `GROWI_OPTIMIZE_MEMORY`: Only `"true"` is valid. Anything else is ignored
+  - `GROWI_LITE_MODE`: Only `"true"` is valid. Anything else is ignored
+  - cgroup v2: `/sys/fs/cgroup/memory.max` — Numeric or `"max"` (unlimited)
+  - cgroup v1: `/sys/fs/cgroup/memory/memory.limit_in_bytes` — Numeric (very large value when unlimited)
+- **Output / destination**: Node flags are passed directly as arguments to `child_process.spawn`
+- **Idempotency & recovery**: Executed on every container restart. Idempotent (`fs.mkdirSync` with `recursive: true` ensures safety)
 
 ##### Environment Variable Interface
 
 | Variable | Type | Default | Description |
 |----------|------|---------|-------------|
-| `GROWI_HEAP_SIZE` | int (MB) | (未設定) | Node.js の --max-heap-size 値を明示指定 |
-| `GROWI_OPTIMIZE_MEMORY` | `"true"` / (未設定) | (未設定) | --optimize-for-size フラグを有効化 |
-| `GROWI_LITE_MODE` | `"true"` / (未設定) | (未設定) | --lite-mode フラグを有効化 |
+| `GROWI_HEAP_SIZE` | int (MB) | (unset) | Explicitly specify the --max-heap-size value for Node.js |
+| `GROWI_OPTIMIZE_MEMORY` | `"true"` / (unset) | (unset) | Enable the --optimize-for-size flag |
+| `GROWI_LITE_MODE` | `"true"` / (unset) | (unset) | Enable the --lite-mode flag |
 
 ##### Heap Size Calculation Logic
 
@@ -394,7 +394,7 @@ chownRecursive('/tmp/page-bulk-export', 1000, 1000);
 fs.chmodSync('/tmp/page-bulk-export', 0o700);
 ```
 
-`chownRecursive` は `fs.readdirSync` + `fs.chownSync` で再帰的に所有者を変更するヘルパー関数。
+`chownRecursive` is a helper function that recursively changes ownership using `fs.readdirSync` + `fs.chownSync`.
 
 ##### Privilege Drop
 
@@ -404,7 +404,7 @@ process.setgid(1000);
 process.setuid(1000);
 ```
 
-`setgid` → `setuid` の順序は必須(setuid 後は setgid できない)。`initgroups` で supplementary groups も初期化。
+The order `setgid` → `setuid` is mandatory (setgid cannot be called after setuid). `initgroups` also initializes supplementary groups.
 
 ##### Migration Execution
 
@@ -418,7 +418,7 @@ execFileSync(process.execPath, [
 ], { stdio: 'inherit', env: { ...process.env, NODE_ENV: 'production' } });
 ```
 
-`execFileSync` はシェルを介さず直接 node バイナリを実行。`npm run migrate` と同等の動作をシェル不要で実現。
+`execFileSync` directly executes the node binary without going through a shell. This achieves equivalent behavior to `npm run migrate` without requiring a shell.
 
 ##### App Process Spawn
 
@@ -443,108 +443,108 @@ child.on('exit', (code: number | null, signal: NodeJS.Signals | null) => {
 ```
 
 **Implementation Notes**
-- TypeScript で記述し、Node.js 24 のネイティブ type stripping で直接実行。`ENTRYPOINT ["node", "/docker-entrypoint.ts"]`
-- enum は使用不可(非 erasable syntax)。interface/type/type annotation のみ使用
-- entrypoint は `process.execPath`(= `/usr/local/bin/node`)を使って migration と app を実行するため、シェルが一切不要
-- `--max-heap-size` は spawn の引数として直接渡されるため、NODE_OPTIONS の制約を回避
-- migration コマンドは `apps/app/package.json` の `migrate` スクリプトの中身を直接記述。package.json の変更時は entrypoint の更新も必要
-- PID 1 の責務: シグナルフォワーディング、子プロセスの reap、正常終了コードの伝播
+- Written in TypeScript and executed directly via Node.js 24 native type stripping. `ENTRYPOINT ["node", "/docker-entrypoint.ts"]`
+- Enums cannot be used (non-erasable syntax). Only interface/type/type annotation are used
+- The entrypoint uses `process.execPath` (= `/usr/local/bin/node`) to execute migration and app, so no shell is needed at all
+- `--max-heap-size` is passed directly as a spawn argument, bypassing NODE_OPTIONS restrictions
+- The migration command directly describes the contents of the `migrate` script from `apps/app/package.json`. When package.json changes, the entrypoint also needs to be updated
+- PID 1 responsibilities: signal forwarding, child process reaping, proper exit code propagation
 
 #### Dockerfile.dockerignore
 
 | Field | Detail |
 |-------|--------|
-| Intent | ビルドコンテキストから不要ファイルを除外 |
+| Intent | Exclude unnecessary files from the build context |
 | Requirements | 4.3 |
 
 **Implementation Notes**
-- 現行に追加すべきエントリ: `.git`, `.env*`(production 以外), `*.md`, `test/`, `**/*.spec.*`, `**/*.test.*`, `.vscode/`, `.idea/`
-- 現行維持: `**/node_modules`, `**/coverage`, `**/Dockerfile`, `**/*.dockerignore`, `**/.pnpm-store`, `**/.next`, `**/.turbo`, `out`, `apps/slackbot-proxy`
+- Entries to add to current: `.git`, `.env*` (except production), `*.md`, `test/`, `**/*.spec.*`, `**/*.test.*`, `.vscode/`, `.idea/`
+- Maintain current: `**/node_modules`, `**/coverage`, `**/Dockerfile`, `**/*.dockerignore`, `**/.pnpm-store`, `**/.next`, `**/.turbo`, `out`, `apps/slackbot-proxy`
 
 ## Error Handling
 
 ### Error Strategy
 
-entrypoint は try-catch で各フェーズのエラーを捕捉。致命的エラーは `process.exit(1)` でコンテナの起動失敗として Docker/k8s に通知。
+The entrypoint catches errors at each phase using try-catch. Fatal errors notify Docker/k8s of container startup failure via `process.exit(1)`.
 
 ### Error Categories and Responses
 
 | Error | Category | Response |
 |-------|----------|----------|
-| cgroup ファイル読み取り失敗 | System | `console.warn` で警告し、フラグなし(V8 デフォルト)で続行 |
-| GROWI_HEAP_SIZE が不正値(NaN 等) | User | `console.error` で警告し、フラグなしで続行(コンテナは起動する) |
-| ディレクトリ作成/権限設定失敗 | System | `process.exit(1)` でコンテナ起動失敗。ボリュームマウント設定を確認 |
-| Migration 失敗 | Business Logic | `execFileSync` が例外を throw → `process.exit(1)`。Docker/k8s が再起動 |
-| アプリプロセス異常終了 | System | 子プロセスの exit code を伝播して `process.exit(code)` |
+| cgroup file read failure | System | Warn with `console.warn` and continue with no flag (V8 default) |
+| GROWI_HEAP_SIZE is invalid (NaN, etc.) | User | Warn with `console.error` and continue with no flag (container still starts) |
+| Directory creation/permission setup failure | System | Container startup failure via `process.exit(1)`. Check volume mount configuration |
+| Migration failure | Business Logic | `execFileSync` throws an exception → `process.exit(1)`. Docker/k8s will restart |
+| App process abnormal exit | System | Propagate child process exit code via `process.exit(code)` |
 
 ## Testing Strategy
 
 ### Unit Tests
-- docker-entrypoint.ts のヒープサイズ算出ロジック: cgroup v2/v1/なし の 3 パターン(TypeScript で型安全にテスト)
-- docker-entrypoint.ts の環境変数組み合わせ: GROWI_HEAP_SIZE + GROWI_OPTIMIZE_MEMORY + GROWI_LITE_MODE
-- docker-entrypoint.ts の chownRecursive ヘルパー: ネストされたディレクトリ構造で正しく再帰 chown されること
-- Node.js 24 の type stripping で docker-entrypoint.ts が直接実行可能なこと
+- Heap size calculation logic in docker-entrypoint.ts: 3 patterns for cgroup v2/v1/none (type-safe testing in TypeScript)
+- Environment variable combinations in docker-entrypoint.ts: GROWI_HEAP_SIZE + GROWI_OPTIMIZE_MEMORY + GROWI_LITE_MODE
+- chownRecursive helper in docker-entrypoint.ts: Verify correct recursive chown on nested directory structures
+- Verify that docker-entrypoint.ts can be directly executed via Node.js 24 type stripping
 
 ### Integration Tests
-- Docker build が成功し、全 5 ステージが完了すること
-- `GROWI_HEAP_SIZE=250` を設定してコンテナ起動し、node プロセスの `--max-heap-size=250` を確認
-- cgroup memory limit 付きでコンテナ起動し、自動算出の `--max-heap-size` が正しいことを確認
-- migration が正常に実行されること(`execFileSync` 経由)
+- Docker build succeeds and all 5 stages complete
+- Start container with `GROWI_HEAP_SIZE=250` set and verify `--max-heap-size=250` on the node process
+- Start container with cgroup memory limit and verify that the auto-calculated `--max-heap-size` is correct
+- Migration executes successfully (via `execFileSync`)
 
 ### E2E Tests
-- `docker compose up` で GROWI + MongoDB が起動し、ブラウザアクセスが可能なこと
-- `FILE_UPLOAD=local` でファイルアップロードが動作すること(/data/uploads の symlink 確認)
-- SIGTERM 送信でコンテナが graceful に停止すること
+- GROWI + MongoDB start via `docker compose up` and browser access is possible
+- File upload works with `FILE_UPLOAD=local` (verify /data/uploads symlink)
+- Container shuts down gracefully when SIGTERM is sent
 
 ## Security Considerations
 
-- **DHI ベースイメージ**: CVE 最大 95% 削減、SLSA Build Level 3 の provenance
-- **シェル不要**: runtime に bash/sh/busybox なし。コマンドインジェクションの攻撃ベクターを排除
-- **gosu/setpriv 不要**: Node.js ネイティブの `process.setuid/setgid` で権限ドロップ。追加バイナリの攻撃面なし
-- **非 root 実行**: アプリケーションは node (UID 1000) で実行。root は entrypoint の初期化(mkdir/chown)のみ
-- **DHI レジストリ認証**: CI/CD で `docker login dhi.io` が必要。Docker Hub 認証情報を使用
+- **DHI base image**: Up to 95% CVE reduction, SLSA Build Level 3 provenance
+- **No shell needed**: No bash/sh/busybox in runtime. Eliminates command injection attack vectors
+- **No gosu/setpriv needed**: Privilege drop via Node.js native `process.setuid/setgid`. No additional binary attack surface
+- **Non-root execution**: Application runs as node (UID 1000). Root is used only for entrypoint initialization (mkdir/chown)
+- **DHI registry authentication**: `docker login dhi.io` is required in CI/CD. Uses Docker Hub credentials
 
 ## Performance & Scalability
 
-- **ビルドキャッシュ**: `turbo prune --docker` により dependency install レイヤーをキャッシュ。ソースコード変更時の再ビルドで依存インストールをスキップ
-- **イメージサイズ**: DHI runtime に追加バイナリなし。node:24-slim 比でベースレイヤーが縮小
-- **メモリ効率**: `--max-heap-size` による total heap 制御で、v24 の trusted_space overhead 問題を回避。マルチテナントでのメモリ圧迫を防止
+- **Build cache**: `turbo prune --docker` caches the dependency install layer. Skips dependency installation during rebuilds when only source code changes
+- **Image size**: No additional binaries in DHI runtime. Base layer is smaller compared to node:24-slim
+- **Memory efficiency**: Total heap control via `--max-heap-size` avoids the v24 trusted_space overhead issue. Prevents memory pressure in multi-tenant environments
 
-## Phase 3: 本番置換と CI/CD 対応
+## Phase 3: Production Replacement and CI/CD Support
 
-### ディレクトリ置換
+### Directory Replacement
 
-`apps/app/docker-new/` の成果物を `apps/app/docker/` に移動し、旧ファイルを削除する。
+Move the artifacts from `apps/app/docker-new/` to `apps/app/docker/` and delete the old files.
 
-**置換対象:**
+**Replacement targets:**
 
-| 操作 | ファイル | 備考 |
+| Operation | File | Notes |
 |------|---------|------|
-| 削除 | `apps/app/docker/Dockerfile` | 旧 3 ステージ Dockerfile(node:20-slim) |
-| 削除 | `apps/app/docker/docker-entrypoint.sh` | 旧 shell entrypoint(gosu 使用) |
-| 削除 | `apps/app/docker/Dockerfile.dockerignore` | 旧 dockerignore |
-| 移動 | `docker-new/Dockerfile` → `docker/Dockerfile` | 新 5 ステージ DHI Dockerfile |
-| 移動 | `docker-new/docker-entrypoint.ts` → `docker/docker-entrypoint.ts` | 新 TypeScript entrypoint |
-| 移動 | `docker-new/docker-entrypoint.spec.ts` → `docker/docker-entrypoint.spec.ts` | テストファイル |
-| 移動 | `docker-new/Dockerfile.dockerignore` → `docker/Dockerfile.dockerignore` | 新 dockerignore |
-| 維持 | `apps/app/docker/codebuild/` | CodeBuild 設定(変更なし) |
-| 維持 | `apps/app/docker/README.md` | Docker Hub README |
-
-**パス参照の更新:**
+| Delete | `apps/app/docker/Dockerfile` | Old 3-stage Dockerfile (node:20-slim) |
+| Delete | `apps/app/docker/docker-entrypoint.sh` | Old shell entrypoint (uses gosu) |
+| Delete | `apps/app/docker/Dockerfile.dockerignore` | Old dockerignore |
+| Move | `docker-new/Dockerfile` → `docker/Dockerfile` | New 5-stage DHI Dockerfile |
+| Move | `docker-new/docker-entrypoint.ts` → `docker/docker-entrypoint.ts` | New TypeScript entrypoint |
+| Move | `docker-new/docker-entrypoint.spec.ts` → `docker/docker-entrypoint.spec.ts` | Test file |
+| Move | `docker-new/Dockerfile.dockerignore` → `docker/Dockerfile.dockerignore` | New dockerignore |
+| Maintain | `apps/app/docker/codebuild/` | CodeBuild configuration (no changes) |
+| Maintain | `apps/app/docker/README.md` | Docker Hub README |
+
+**Path reference updates:**
 - Dockerfile line 122: `apps/app/docker-new/docker-entrypoint.ts` → `apps/app/docker/docker-entrypoint.ts`
 
-**影響を受けない既存参照(コードベース調査済み):**
-- `buildspec.yml`: `-f ./apps/app/docker/Dockerfile` — パスは同一のまま
-- `codebuild.tf`: `buildspec = "apps/app/docker/codebuild/buildspec.yml"` — 同一
-- `.github/workflows/release.yml`: `./apps/app/docker/README.md` — 同一
-- `.github/workflows/ci-app.yml`: `!apps/app/docker/**` 除外パターン — 同一
-- `apps/app/bin/github-actions/update-readme.sh`: `cd docker` — 同一
+**Existing references not affected (verified via codebase investigation):**
+- `buildspec.yml`: `-f ./apps/app/docker/Dockerfile` — Path remains the same
+- `codebuild.tf`: `buildspec = "apps/app/docker/codebuild/buildspec.yml"` — Same
+- `.github/workflows/release.yml`: `./apps/app/docker/README.md` — Same
+- `.github/workflows/ci-app.yml`: `!apps/app/docker/**` exclusion pattern — Same
+- `apps/app/bin/github-actions/update-readme.sh`: `cd docker` — Same
 
-### buildspec.yml の DHI レジストリ認証
+### buildspec.yml DHI Registry Authentication
 
-DHI イメージの pull に `docker login dhi.io` が必要。[DHI ドキュメント](https://docs.docker.com/dhi/how-to/use/)によると、DHI は Docker Hub 認証情報を使用する。
+`docker login dhi.io` is required for pulling DHI images. According to the [DHI documentation](https://docs.docker.com/dhi/how-to/use/), DHI uses Docker Hub credentials.
 
-**現行 buildspec.yml:**
+**Current buildspec.yml:**
 ```yaml
 phases:
   pre_build:
@@ -552,7 +552,7 @@ phases:
       - echo ${DOCKER_REGISTRY_PASSWORD} | docker login --username growimoogle --password-stdin
 ```
 
-**更新後:**
+**Updated:**
 ```yaml
 phases:
   pre_build:
@@ -563,6 +563,6 @@ phases:
       - echo ${DOCKER_REGISTRY_PASSWORD} | docker login dhi.io --username growimoogle --password-stdin
 ```
 
-- Docker Hub と同一の認証情報を使用(DHI は Docker Hub アカウントで認証)
-- 既存の `DOCKER_REGISTRY_PASSWORD` シークレットを再利用
-- `secretsmanager.tf` の変更は不要
+- Uses the same credentials as Docker Hub (DHI authenticates with Docker Hub accounts)
+- Reuses the existing `DOCKER_REGISTRY_PASSWORD` secret
+- No changes needed to `secretsmanager.tf`

+ 82 - 82
.kiro/specs/official-docker-image/requirements.md

@@ -2,131 +2,131 @@
 
 ## Introduction
 
-GROWI 公式 Docker イメージの Dockerfile (`apps/app/docker/Dockerfile`) および `docker-entrypoint.sh` を、2025-2026 年のベストプラクティスに基づきモダナイズ・最適化する。Node.js 24 をターゲットとし、メモリレポート (`apps/app/tmp/memory-results/REPORT.md`) の知見を反映してメモリ管理を改善する。
-
-### 現状分析の要約
-
-**現行 Dockerfile の構成:**
-- 3 ステージ構成: `base` → `builder` → `release`(node:20-slim ベース)
-- pnpm + turbo によるモノレポビルド、`pnpm deploy` による本番依存抽出
-- gosu を使った root → node ユーザーへの権限ドロップ(entrypoint でディレクトリ作成後)
-- `COPY . .` でコンテキスト全体をビルダーにコピー
-- CMD 内で `npm run migrate` 実行後にアプリ起動
-
-**GROWI 固有の設計意図(維持すべき事項):**
-- 権限ドロップパターン: entrypoint が root 権限で `/data/uploads` や `/tmp/page-bulk-export` を作成・権限設定した後、node ユーザーに降格して実行する必要がある
-- `pnpm deploy --prod`: pnpm モノレポから本番依存のみを抽出するための公式手法
-- tar.gz によるステージ間アーティファクト受け渡し: ビルド成果物を cleanly に release ステージに転送
-- `apps/app/tmp` ディレクトリ: 運用中にファイルが配置されるため本番イメージに必要
-- `--expose_gc` フラグ: バッチ処理(ES rebuild、import 等)で明示的に `gc()` を呼び出すために必要
-- CMD 内の `npm run migrate`: Docker image ユーザーの利便性のため、起動時にマイグレーションを自動実行
-
-**参考資料:**
-- [Future Architect: 2024年版 Dockerfile ベストプラクティス](https://future-architect.github.io/articles/20240726a/)
+Modernize and optimize the GROWI official Docker image's Dockerfile (`apps/app/docker/Dockerfile`) and `docker-entrypoint.sh` based on 2025-2026 best practices. Target Node.js 24 and incorporate findings from the memory report (`apps/app/tmp/memory-results/REPORT.md`) to improve memory management.
+
+### Summary of Current State Analysis
+
+**Current Dockerfile structure:**
+- 3-stage structure: `base` → `builder` → `release` (based on node:20-slim)
+- Monorepo build with pnpm + turbo, production dependency extraction via `pnpm deploy`
+- Privilege drop from root to node user using gosu (after directory creation in entrypoint)
+- `COPY . .` copies the entire context into the builder
+- Application starts after running `npm run migrate` in CMD
+
+**GROWI-specific design intentions (items to maintain):**
+- Privilege drop pattern: The entrypoint must create and set permissions for `/data/uploads` and `/tmp/page-bulk-export` with root privileges, then drop to the node user for execution
+- `pnpm deploy --prod`: The official method for extracting only production dependencies from a pnpm monorepo
+- Inter-stage artifact transfer via tar.gz: Cleanly transfers build artifacts to the release stage
+- `apps/app/tmp` directory: Required in the production image as files are placed there during operation
+- `--expose_gc` flag: Required for explicitly calling `gc()` in batch processing (ES rebuild, import, etc.)
+- `npm run migrate` in CMD: Automatically runs migrations at startup for the convenience of Docker image users
+
+**References:**
+- [Future Architect: 2024 Dockerfile Best Practices](https://future-architect.github.io/articles/20240726a/)
 - [Snyk: 10 best practices to containerize Node.js](https://snyk.io/blog/10-best-practices-to-containerize-nodejs-web-applications-with-docker/)
 - [ByteScrum: Dockerfile Best Practices 2025](https://blog.bytescrum.com/dockerfile-best-practices-2025-secure-fast-and-modern)
 - [OneUptime: Docker Health Check Best Practices 2026](https://oneuptime.com/blog/post/2026-01-30-docker-health-check-best-practices/view)
 - [Docker: Introduction to heredocs in Dockerfiles](https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/)
-- [Docker Hardened Images: Node.js 移行ガイド](https://docs.docker.com/dhi/migration/examples/node/)
-- [Docker Hardened Images カタログ: Node.js](https://hub.docker.com/hardened-images/catalog/dhi/node)
-- GROWI メモリ使用量調査レポート (`apps/app/tmp/memory-results/REPORT.md`)
+- [Docker Hardened Images: Node.js Migration Guide](https://docs.docker.com/dhi/migration/examples/node/)
+- [Docker Hardened Images Catalog: Node.js](https://hub.docker.com/hardened-images/catalog/dhi/node)
+- GROWI Memory Usage Investigation Report (`apps/app/tmp/memory-results/REPORT.md`)
 
 ## Requirements
 
-### Requirement 1: ベースイメージとビルド環境のモダナイズ
+### Requirement 1: Modernize Base Image and Build Environment
 
-**Objective:** As an インフラ管理者, I want Dockerfile のベースイメージと構文が最新のベストプラクティスに準拠していること, so that セキュリティパッチの適用・パフォーマンス向上・メンテナンス性の改善が得られる
+**Objective:** As an infrastructure administrator, I want the Dockerfile's base image and syntax to comply with the latest best practices, so that security patch application, performance improvements, and maintainability enhancements are achieved
 
 #### Acceptance Criteria
 
-1. The Dockerfile shall ベースイメージとして Docker Hardened Images(DHI)を使用する。ビルドステージには `dhi.io/node:24-debian13-dev`、リリースステージには `dhi.io/node:24-debian13` を使用する(glibc ベースでパフォーマンス維持、CVE 最大 95% 削減)
-2. The Dockerfile shall syntax ディレクティブを `# syntax=docker/dockerfile:1`(最新安定版を自動追従)に更新する
-3. The Dockerfile shall pnpm のインストールに wget スタンドアロンスクリプト方式を維持する(corepack は Node.js 25 以降で同梱廃止のため不採用)
-4. The Dockerfile shall `pnpm install ---frozen-lockfile`(ダッシュ3つ)の typo を `--frozen-lockfile`(ダッシュ2つ)に修正する
-5. The Dockerfile shall pnpm バージョンのハードコードを避け、`package.json` の `packageManager` フィールドまたはインストールスクリプトの最新版取得を活用する
+1. The Dockerfile shall use Docker Hardened Images (DHI) as the base image. Use `dhi.io/node:24-debian13-dev` for the build stage and `dhi.io/node:24-debian13` for the release stage (glibc-based for performance retention, up to 95% CVE reduction)
+2. The Dockerfile shall update the syntax directive to `# syntax=docker/dockerfile:1` (automatically follows the latest stable version)
+3. The Dockerfile shall maintain the wget standalone script method for pnpm installation (corepack is not adopted because it will be removed from Node.js 25 onwards)
+4. The Dockerfile shall fix the typo `pnpm install ---frozen-lockfile` (three dashes) to `--frozen-lockfile` (two dashes)
+5. The Dockerfile shall avoid hardcoding the pnpm version and leverage the `packageManager` field in `package.json` or the latest version retrieval from the install script
 
-### Requirement 2: メモリ管理の最適化
+### Requirement 2: Memory Management Optimization
 
-**Objective:** As a GROWI 運用者, I want コンテナのメモリ制約に応じて Node.js のヒープサイズが適切に制御されること, so that OOMKilled のリスクが低減し、マルチテナント環境でのメモリ効率が向上する
+**Objective:** As a GROWI operator, I want the Node.js heap size to be appropriately controlled according to container memory constraints, so that the risk of OOMKilled is reduced and memory efficiency in multi-tenant environments is improved
 
 #### Acceptance Criteria
 
-1. The docker-entrypoint.ts shall `GROWI_HEAP_SIZE` 環境変数が設定されている場合、その値を `--max-heap-size` フラグとして node プロセスに渡す
-2. While `GROWI_HEAP_SIZE` 環境変数が未設定の場合, the docker-entrypoint.ts shall cgroup メモリリミット(v2: `/sys/fs/cgroup/memory.max`、v1: `/sys/fs/cgroup/memory/memory.limit_in_bytes`)を読み取り、その 60% を `--max-heap-size` として自動算出する
-3. While cgroup メモリリミットが検出できない(ベアメタル等)かつ `GROWI_HEAP_SIZE` が未設定の場合, the docker-entrypoint.ts shall `--max-heap-size` フラグを付与せず、V8 のデフォルト動作に委ねる
-4. When `GROWI_OPTIMIZE_MEMORY` 環境変数が `true` に設定された場合, the docker-entrypoint.ts shall `--optimize-for-size` フラグを node プロセスに追加する
-5. When `GROWI_LITE_MODE` 環境変数が `true` に設定された場合, the docker-entrypoint.ts shall `--lite-mode` フラグを node プロセスに追加する(TurboFan 無効化により RSS を v20 同等まで削減。OOMKilled 頻発時の最終手段として使用)
-6. The docker-entrypoint.ts shall `--max-heap-size` を使用し、`--max_old_space_size` は使用しない(Node.js 24 の trusted_space overhead 問題を回避するため)
-7. The docker-entrypoint.ts shall `--max-heap-size` を `NODE_OPTIONS` ではなく node コマンドの直接引数として渡す(Node.js の制約)
+1. The docker-entrypoint.ts shall pass the value of the `GROWI_HEAP_SIZE` environment variable as the `--max-heap-size` flag to the node process when it is set
+2. While the `GROWI_HEAP_SIZE` environment variable is not set, the docker-entrypoint.ts shall read the cgroup memory limit (v2: `/sys/fs/cgroup/memory.max`, v1: `/sys/fs/cgroup/memory/memory.limit_in_bytes`) and automatically calculate 60% of it as `--max-heap-size`
+3. While the cgroup memory limit cannot be detected (e.g., bare metal) and `GROWI_HEAP_SIZE` is not set, the docker-entrypoint.ts shall not add the `--max-heap-size` flag and defer to V8's default behavior
+4. When the `GROWI_OPTIMIZE_MEMORY` environment variable is set to `true`, the docker-entrypoint.ts shall add the `--optimize-for-size` flag to the node process
+5. When the `GROWI_LITE_MODE` environment variable is set to `true`, the docker-entrypoint.ts shall add the `--lite-mode` flag to the node process (disables TurboFan to reduce RSS to v20-equivalent levels. Used as a last resort when OOMKilled occurs frequently)
+6. The docker-entrypoint.ts shall use `--max-heap-size` and shall not use `--max_old_space_size` (to avoid the trusted_space overhead issue in Node.js 24)
+7. The docker-entrypoint.ts shall pass `--max-heap-size` as a direct argument to the node command, not via `NODE_OPTIONS` (due to Node.js constraints)
 
-### Requirement 3: ビルド効率とキャッシュの最適化
+### Requirement 3: Build Efficiency and Cache Optimization
 
-**Objective:** As a 開発者, I want Docker ビルドが高速かつ効率的であること, so that CI/CD パイプラインのビルド時間が短縮され、イメージサイズが最小化される
+**Objective:** As a developer, I want Docker builds to be fast and efficient, so that CI/CD pipeline build times are reduced and image size is minimized
 
 #### Acceptance Criteria
 
-1. The Dockerfile shall builder ステージで `COPY . .` の代わりに `--mount=type=bind` を使用し、ソースコードをレイヤーに含めない
-2. The Dockerfile shall pnpm store のキャッシュマウント (`--mount=type=cache,target=...`) を維持する
-3. The Dockerfile shall ビルドステージで apt-get のキャッシュマウントを維持する
-4. The Dockerfile shall release ステージで `.next/cache` が含まれないことを保証する
-5. The Dockerfile shall ビルドステージからリリースステージへのアーティファクト転送に `--mount=type=bind,from=builder` パターンを使用する
+1. The Dockerfile shall use `--mount=type=bind` instead of `COPY . .` in the builder stage to avoid including source code in layers
+2. The Dockerfile shall maintain pnpm store cache mounts (`--mount=type=cache,target=...`)
+3. The Dockerfile shall maintain apt-get cache mounts in the build stage
+4. The Dockerfile shall ensure that `.next/cache` is not included in the release stage
+5. The Dockerfile shall use the `--mount=type=bind,from=builder` pattern for artifact transfer from the build stage to the release stage
 
-### Requirement 4: セキュリティ強化
+### Requirement 4: Security Hardening
 
-**Objective:** As a セキュリティ担当者, I want Docker イメージがセキュリティベストプラクティスに準拠していること, so that 攻撃面が最小化され、本番環境の安全性が向上する
+**Objective:** As a security officer, I want the Docker image to comply with security best practices, so that the attack surface is minimized and the safety of the production environment is improved
 
 #### Acceptance Criteria
 
-1. The Dockerfile shall 非 root ユーザー(node)でアプリケーションを実行する(Node.js entrypoint で `process.setuid/setgid` を使用)
-2. The Dockerfile shall release ステージに不要なパッケージ(wget、curl 等のビルドツール)をインストールしない
-3. The Dockerfile shall `.dockerignore` により、`.git`、`node_modules`、テストファイル、シークレットファイル等がビルドコンテキストに含まれないことを保証する
-4. The Dockerfile shall `apt-get install` で `--no-install-recommends` を使用して不要な推奨パッケージのインストールを防ぐ
-5. The Dockerfile shall release ステージのイメージに、ビルド時にのみ必要なツール(turbo、node-gyp、pnpm 等)を含めない
+1. The Dockerfile shall run the application as a non-root user (node) (using `process.setuid/setgid` in the Node.js entrypoint)
+2. The Dockerfile shall not install unnecessary packages (build tools such as wget, curl, etc.) in the release stage
+3. The Dockerfile shall ensure that `.git`, `node_modules`, test files, secret files, etc. are not included in the build context via `.dockerignore`
+4. The Dockerfile shall use `--no-install-recommends` with `apt-get install` to prevent installation of unnecessary recommended packages
+5. The Dockerfile shall not include tools only needed at build time (turbo, node-gyp, pnpm, etc.) in the release stage image
 
-### Requirement 5: 運用性・可観測性の向上
+### Requirement 5: Operability and Observability Improvement
 
-**Objective:** As a 運用担当者, I want Docker イメージに適切なメタデータが設定されていること, so that コンテナオーケストレーターによる管理が容易になる
+**Objective:** As an operations engineer, I want the Docker image to have appropriate metadata configured, so that management by container orchestrators is facilitated
 
 #### Acceptance Criteria
 
-1. The Dockerfile shall OCI 標準の LABEL アノテーション(`org.opencontainers.image.source`、`org.opencontainers.image.title`、`org.opencontainers.image.description`、`org.opencontainers.image.vendor`)を含める
-2. The Dockerfile shall `EXPOSE 3000` を維持してポートをドキュメント化する
-3. The Dockerfile shall `VOLUME /data` を維持してデータ永続化ポイントをドキュメント化する
+1. The Dockerfile shall include OCI standard LABEL annotations (`org.opencontainers.image.source`, `org.opencontainers.image.title`, `org.opencontainers.image.description`, `org.opencontainers.image.vendor`)
+2. The Dockerfile shall maintain `EXPOSE 3000` to document the port
+3. The Dockerfile shall maintain `VOLUME /data` to document the data persistence point
 
-### Requirement 6: entrypoint と CMD のリファクタリング
+### Requirement 6: Entrypoint and CMD Refactoring
 
-**Objective:** As a 開発者, I want entrypoint スクリプトと CMD が明確で保守しやすい構造であること, so that メモリフラグの動的組み立てや将来の拡張が容易になる
+**Objective:** As a developer, I want the entrypoint script and CMD to have a clear and maintainable structure, so that dynamic assembly of memory flags and future extensions are facilitated
 
 #### Acceptance Criteria
 
-1. The docker-entrypoint.ts shall ヒープサイズ算出ロジック(Requirement 2 の 3 段フォールバック)を含める
-2. The docker-entrypoint.ts shall 算出されたフラグを node コマンドの引数として組み立て、`process.setgid` + `process.setuid` で権限ドロップ後に `child_process.spawn` で実行する
-3. The docker-entrypoint.ts shall `/data/uploads` のディレクトリ作成・シンボリックリンク・権限設定(FILE_UPLOAD=local サポート)を維持する
-4. The docker-entrypoint.ts shall `/tmp/page-bulk-export` のディレクトリ作成・権限設定を維持する
-5. The docker-entrypoint.ts shall マイグレーション実行後にアプリケーションを起動する現行動作を維持する
-6. The docker-entrypoint.ts shall `--expose_gc` フラグを維持する(バッチ処理での明示的 GC 呼び出しに必要)
-7. When `GROWI_HEAP_SIZE`、cgroup 算出値、または各種最適化フラグが設定された場合, the docker-entrypoint.ts shall 適用されたフラグの内容を標準出力にログ出力する
-8. The docker-entrypoint.ts shall TypeScript で記述し、Node.js 24 のネイティブ TypeScript 実行機能(type stripping)で直接実行する
+1. The docker-entrypoint.ts shall include the heap size calculation logic (3-tier fallback from Requirement 2)
+2. The docker-entrypoint.ts shall assemble the calculated flags as node command arguments and execute via `child_process.spawn` after dropping privileges with `process.setgid` + `process.setuid`
+3. The docker-entrypoint.ts shall maintain directory creation, symbolic link setup, and permission configuration for `/data/uploads` (FILE_UPLOAD=local support)
+4. The docker-entrypoint.ts shall maintain directory creation and permission configuration for `/tmp/page-bulk-export`
+5. The docker-entrypoint.ts shall maintain the current behavior of starting the application after running migrations
+6. The docker-entrypoint.ts shall maintain the `--expose_gc` flag (required for explicit GC calls in batch processing)
+7. When `GROWI_HEAP_SIZE`, cgroup-calculated value, or various optimization flags are set, the docker-entrypoint.ts shall log the content of the applied flags to standard output
+8. The docker-entrypoint.ts shall be written in TypeScript and executed directly using Node.js 24's native TypeScript execution feature (type stripping)
 
-### Requirement 7: 後方互換性
+### Requirement 7: Backward Compatibility
 
-**Objective:** As a 既存の Docker image ユーザー, I want 新しい Dockerfile に移行しても既存の運用が壊れないこと, so that アップグレード時のリスクが最小化される
+**Objective:** As an existing Docker image user, I want existing operations to not break when migrating to the new Dockerfile, so that the risk during upgrades is minimized
 
 #### Acceptance Criteria
 
-1. The Docker イメージ shall 環境変数によるアプリケーション設定(`MONGO_URI`、`FILE_UPLOAD` 等)を従来通りサポートする
-2. The Docker イメージ shall `VOLUME /data` を維持し、既存のデータボリュームマウントとの互換性を保つ
-3. The Docker イメージ shall ポート 3000 でリッスンする現行動作を維持する
-4. While メモリ管理の環境変数(`GROWI_HEAP_SIZE`、`GROWI_OPTIMIZE_MEMORY`、`GROWI_LITE_MODE`)が未設定の場合, the Docker イメージ shall 既存の動作(Node.js 24 のデフォルト)と実質的に同等に動作する
-5. The Docker イメージ shall `docker-compose.yml` / `compose.yaml` からの利用パターンを維持する
+1. The Docker image shall support application configuration via environment variables (`MONGO_URI`, `FILE_UPLOAD`, etc.) as before
+2. The Docker image shall maintain `VOLUME /data` and preserve compatibility with existing data volume mounts
+3. The Docker image shall maintain the current behavior of listening on port 3000
+4. While memory management environment variables (`GROWI_HEAP_SIZE`, `GROWI_OPTIMIZE_MEMORY`, `GROWI_LITE_MODE`) are not set, the Docker image shall behave substantially equivalent to the existing behavior (Node.js 24 defaults)
+5. The Docker image shall maintain the usage pattern from `docker-compose.yml` / `compose.yaml`
 
-### Requirement 8: 本番置換と CI/CD 対応
+### Requirement 8: Production Replacement and CI/CD Support
 
-**Objective:** As an インフラ管理者, I want docker-new ディレクトリの成果物が既存の docker ディレクトリを正式に置き換え、CI/CD パイプラインが新しい Dockerfile で動作すること, so that 本番ビルドで DHI ベースのイメージが使用される
+**Objective:** As an infrastructure administrator, I want the artifacts in the docker-new directory to officially replace the existing docker directory and the CI/CD pipeline to operate with the new Dockerfile, so that DHI-based images are used in production builds
 
 #### Acceptance Criteria
 
-1. The Docker ビルド構成 shall `apps/app/docker-new/` の全ファイル(`Dockerfile`、`docker-entrypoint.ts`、`docker-entrypoint.spec.ts`、`Dockerfile.dockerignore`)を `apps/app/docker/` に移動し、旧ファイル(旧 `Dockerfile`、`docker-entrypoint.sh`、旧 `Dockerfile.dockerignore`)を削除する。`codebuild/` ディレクトリと `README.md` は維持する
-2. The Dockerfile shall ファイル内の自己参照パス `apps/app/docker-new/docker-entrypoint.ts` を `apps/app/docker/docker-entrypoint.ts` に更新する
-3. The buildspec.yml shall DHI レジストリ(`dhi.io`)へのログインコマンドを pre_build フェーズに追加する。DHI は Docker Hub 認証情報を使用するため、既存の `DOCKER_REGISTRY_PASSWORD` シークレットを再利用する
-4. The buildspec.yml shall 新しい Dockerfile のパス(`./apps/app/docker/Dockerfile`)を正しく参照する(現行パスと同一のため変更不要であることを確認する)
+1. The Docker build configuration shall move all files from `apps/app/docker-new/` (`Dockerfile`, `docker-entrypoint.ts`, `docker-entrypoint.spec.ts`, `Dockerfile.dockerignore`) to `apps/app/docker/`, and delete the old files (old `Dockerfile`, `docker-entrypoint.sh`, old `Dockerfile.dockerignore`). The `codebuild/` directory and `README.md` shall be maintained
+2. The Dockerfile shall update the self-referencing path `apps/app/docker-new/docker-entrypoint.ts` to `apps/app/docker/docker-entrypoint.ts`
+3. The buildspec.yml shall add a login command to the DHI registry (`dhi.io`) in the pre_build phase. Since DHI uses Docker Hub credentials, the existing `DOCKER_REGISTRY_PASSWORD` secret shall be reused
+4. The buildspec.yml shall correctly reference the new Dockerfile path (`./apps/app/docker/Dockerfile`) (verify that no change is needed as it is the same as the current path)

+ 153 - 153
.kiro/specs/official-docker-image/research.md

@@ -6,236 +6,236 @@
 
 ## Summary
 - **Feature**: `official-docker-image`
-- **Discovery Scope**: Extension(既存 Dockerfile の大幅な改善)
+- **Discovery Scope**: Extension (major improvement of existing Dockerfile)
 - **Key Findings**:
-  - DHI runtime image (`dhi.io/node:24-debian13`) はシェル・パッケージマネージャ・coreutils を含まない極小構成。Node.js entrypoint(TypeScript)を採用し、シェル・追加バイナリ一切不要の構成を実現
-  - `--mount=type=bind` はモノレポのマルチステップビルドでは非実用的。`turbo prune --docker` が Turborepo 公式推奨のDocker最適化手法
-  - gosu は Node.js ネイティブの `process.setuid/setgid` で置き換え。外部バイナリ(gosu/setpriv/busybox)が完全に不要
-  - HEALTHCHECK は不採用(k8s は独自 probe を使用。Docker Compose ユーザーは自前で設定可能)
-  - Node.js 24 は TypeScript ネイティブ実行(type stripping)をサポート。entrypoint を TypeScript で記述可能
+  - The DHI runtime image (`dhi.io/node:24-debian13`) is a minimal configuration that does not include a shell, package manager, or coreutils. By adopting a Node.js entrypoint (TypeScript), a configuration requiring no shell or additional binaries is achieved
+  - `--mount=type=bind` is impractical for monorepo multi-step builds. `turbo prune --docker` is the officially recommended Docker optimization approach by Turborepo
+  - gosu is replaced by Node.js native `process.setuid/setgid`. External binaries (gosu/setpriv/busybox) are completely unnecessary
+  - HEALTHCHECK is not adopted (k8s uses its own probes. Docker Compose users can configure it themselves)
+  - Node.js 24 supports native TypeScript execution (type stripping). The entrypoint can be written in TypeScript
 
 ## Research Log
 
-### DHI Runtime Image の構成
+### DHI Runtime Image Configuration
 
-- **Context**: `dhi.io/node:24-debian13` をリリースステージのベースイメージとして採用する際の制約調査
+- **Context**: Investigation of constraints when adopting `dhi.io/node:24-debian13` as the base image for the release stage
 - **Sources Consulted**:
-  - [DHI Catalog GitHub](https://github.com/docker-hardened-images/catalog) — `image/node/debian-13/` ディレクトリ
+  - [DHI Catalog GitHub](https://github.com/docker-hardened-images/catalog) — `image/node/debian-13/` directory
   - [DHI Documentation](https://docs.docker.com/dhi/)
   - [DHI Use an Image](https://docs.docker.com/dhi/how-to/use/)
 - **Findings**:
-  - Runtime image のプリインストールパッケージ: `base-files`, `ca-certificates`, `libc6`, `libgomp1`, `libstdc++6`, `netbase`, `tzdata` のみ
-  - **シェルなし**、**apt なし**、**coreutils なし**、**curl/wget なし**
-  - デフォルトユーザー: `node` (UID 1000, GID 1000)
-  - Dev image (`-dev`): `apt`, `bash`, `git`, `util-linux`, `coreutils` 等がプリインストール
-  - 利用可能タグ: `dhi.io/node:24-debian13`, `dhi.io/node:24-debian13-dev`
-  - プラットフォーム: `linux/amd64`, `linux/arm64`
+  - Pre-installed packages in the runtime image: only `base-files`, `ca-certificates`, `libc6`, `libgomp1`, `libstdc++6`, `netbase`, `tzdata`
+  - **No shell**, **no apt**, **no coreutils**, **no curl/wget**
+  - Default user: `node` (UID 1000, GID 1000)
+  - Dev image (`-dev`): `apt`, `bash`, `git`, `util-linux`, `coreutils`, etc. are pre-installed
+  - Available tags: `dhi.io/node:24-debian13`, `dhi.io/node:24-debian13-dev`
+  - Platforms: `linux/amd64`, `linux/arm64`
 - **Implications**:
-  - entrypoint を Node.js(TypeScript)で記述することで、シェルも追加バイナリも完全に不要
-  - gosu/setpriv は Node.js ネイティブの `process.setuid/setgid` で代替。外部バイナリのコピーが不要
-  - HEALTHCHECK は不採用(k8s は独自 probe を使用)。curl/Node.js http モジュールによるヘルスチェックは不要
+  - By writing the entrypoint in Node.js (TypeScript), neither a shell nor additional binaries are needed at all
+  - gosu/setpriv are replaced by Node.js native `process.setuid/setgid`. No need to copy external binaries
+  - HEALTHCHECK is not adopted (k8s uses its own probes). Health checks via curl/Node.js http module are unnecessary
 
-### `--mount=type=bind` のモノレポビルドでの適用性
+### Applicability of `--mount=type=bind` in Monorepo Builds
 
-- **Context**: Requirement 3.1「builder ステージで `COPY . .` の代わりに `--mount=type=bind` を使用」の実現可能性調査
+- **Context**: Investigation of the feasibility of Requirement 3.1 "Use `--mount=type=bind` instead of `COPY . .` in the builder stage"
 - **Sources Consulted**:
   - [Docker Build Cache Optimization](https://docs.docker.com/build/cache/optimize/)
   - [Dockerfile Reference - RUN --mount](https://docs.docker.com/reference/dockerfile/)
   - [pnpm Docker Documentation](https://pnpm.io/docker)
   - [Turborepo Docker Guide](https://turbo.build/repo/docs/handbook/deploying-with-docker)
 - **Findings**:
-  - `--mount=type=bind` は **RUN 命令の実行中のみ有効** で、次の RUN 命令には引き継がれない
-  - モノレポビルドの multi-step プロセス(install → build → deploy)では、各ステップが前のステップの成果物に依存するため、bind mount だけでは実現困難
-  - 全ステップを単一 RUN にまとめることは可能だが、レイヤーキャッシュの利点が失われる
-  - **Turborepo 公式推奨**: `turbo prune --docker` で Docker 用にモノレポを最小化
-    - `out/json/` — dependency install に必要な package.json のみ
+  - `--mount=type=bind` is **only valid during the execution of a RUN instruction** and is not carried over to the next RUN instruction
+  - In the multi-step process of monorepo builds (install -> build -> deploy), each step depends on artifacts from the previous step, making it difficult to achieve with bind mounts alone
+  - It is possible to combine all steps into a single RUN, but this loses the benefits of layer caching
+  - **Turborepo official recommendation**: Use `turbo prune --docker` to minimize the monorepo for Docker
+    - `out/json/` — only package.json files needed for dependency install
     - `out/pnpm-lock.yaml` — lockfile
-    - `out/full/` — ビルドに必要なソースコード
-  - この方式により `COPY . .` を回避しつつ、レイヤーキャッシュを活用可能
+    - `out/full/` — source code needed for the build
+  - This approach avoids `COPY . .` while leveraging layer caching
 - **Implications**:
-  - Requirement 3.1 は `--mount=type=bind` ではなく `turbo prune --docker` パターンで実現すべき
-  - 目標(ソースコードのレイヤー最小化・キャッシュ効率向上)は同等に達成可能
-  - **ただし** `turbo prune --docker` の pnpm workspace との互換性は実装時に検証が必要
+  - Requirement 3.1 should be achieved using the `turbo prune --docker` pattern instead of `--mount=type=bind`
+  - The goal (minimizing source code layers / improving cache efficiency) can be equally achieved
+  - **However**, compatibility of `turbo prune --docker` with pnpm workspaces needs to be verified during implementation
 
-### gosu の代替手段
+### Alternatives to gosu
 
-- **Context**: DHI runtime image で gosu が利用できないため、代替手段を調査
+- **Context**: Investigation of alternatives since gosu is not available in the DHI runtime image
 - **Sources Consulted**:
-  - [gosu GitHub](https://github.com/tianon/gosu) — 代替ツール一覧
+  - [gosu GitHub](https://github.com/tianon/gosu) — list of alternative tools
   - [Debian Packages - gosu in trixie](https://packages.debian.org/trixie/admin/gosu)
   - [PhotoPrism: Switch from gosu to setpriv](https://github.com/photoprism/photoprism/pull/2730)
   - [MongoDB Docker: Replace gosu by setpriv](https://github.com/docker-library/mongo/pull/714)
   - Node.js `process.setuid/setgid` documentation
 - **Findings**:
-  - `setpriv` は `util-linux` の一部で、DHI dev image にプリインストール済み
-  - `gosu node command`  `setpriv --reuid=node --regid=node --init-groups -- command` に置換可能
-  - PhotoPrism、MongoDB 公式 Docker image が gosu → setpriv に移行済み
-  - **Node.js ネイティブ**: `process.setgid(1000)` + `process.setuid(1000)` + `process.initgroups('node', 1000)` で完全に代替可能
-  - Node.js entrypoint を採用する場合、外部バイナリ(gosu/setpriv/busybox)が一切不要
+  - `setpriv` is part of `util-linux` and is pre-installed in the DHI dev image
+  - `gosu node command` can be replaced with `setpriv --reuid=node --regid=node --init-groups -- command`
+  - PhotoPrism and the official MongoDB Docker image have already migrated from gosu to setpriv
+  - **Node.js native**: Can be fully replaced with `process.setgid(1000)` + `process.setuid(1000)` + `process.initgroups('node', 1000)`
+  - When adopting a Node.js entrypoint, no external binaries (gosu/setpriv/busybox) are needed at all
 - **Implications**:
-  - **最終決定**: Node.js ネイティブの `process.setuid/setgid` を採用(setpriv も不要)
-  - gosu/setpriv バイナリのコピーが不要になり、release ステージに追加バイナリなし
-  - DHI runtime の攻撃面最小化をそのまま維持
+  - **Final decision**: Adopt Node.js native `process.setuid/setgid` (setpriv is also unnecessary)
+  - No need to copy gosu/setpriv binaries, resulting in no additional binaries in the release stage
+  - Maintains the minimized attack surface of the DHI runtime as-is
 
-### HEALTHCHECK の実装方式(不採用)
+### HEALTHCHECK Implementation Approach (Not Adopted)
 
-- **Context**: DHI runtime image に curl がないため、HEALTHCHECK の実装方式を調査
+- **Context**: Investigation of HEALTHCHECK implementation approaches since curl is not available in the DHI runtime image
 - **Sources Consulted**:
   - [Docker Healthchecks in Distroless Node.js](https://www.mattknight.io/blog/docker-healthchecks-in-distroless-node-js)
   - [Docker Healthchecks: Why Not to Use curl](https://blog.sixeyed.com/docker-healthchecks-why-not-to-use-curl-or-iwr/)
   - GROWI healthcheck endpoint: `apps/app/src/server/routes/apiv3/healthcheck.ts`
 - **Findings**:
-  - Node.js の `http` モジュールで十分(curl は不要)
-  - GROWI の `/_api/v3/healthcheck` エンドポイントはパラメータなしで `{ status: 'OK' }` を返す
-  - Docker HEALTHCHECK は Docker Compose の `depends_on: service_healthy` 依存順序制御に有用
-  - k8s 環境では独自 probe(liveness/readiness)を使用するため Dockerfile の HEALTHCHECK は不要
+  - Node.js `http` module is sufficient (curl is unnecessary)
+  - GROWI's `/_api/v3/healthcheck` endpoint returns `{ status: 'OK' }` without any parameters
+  - Docker HEALTHCHECK is useful for Docker Compose's `depends_on: service_healthy` dependency order control
+  - In k8s environments, custom probes (liveness/readiness) are used, so the Dockerfile's HEALTHCHECK is unnecessary
 - **Implications**:
-  - **最終決定: 不採用**。k8s は独自 probe を使用し、Docker Compose ユーザーは compose.yaml で自前設定可能
-  - Dockerfile に HEALTHCHECK を含めないことで、シンプルさを維持
+  - **Final decision: Not adopted**. k8s uses its own probes, and Docker Compose users can configure it themselves in compose.yaml
+  - By not including HEALTHCHECK in the Dockerfile, simplicity is maintained
 
-### npm run migrate のシェル依存性
+### Shell Dependency of npm run migrate
 
-- **Context**: CMD 内の `npm run migrate` が shell を必要とするかの調査
+- **Context**: Investigation of whether `npm run migrate` within CMD requires a shell
 - **Sources Consulted**:
-  - GROWI `apps/app/package.json` の `migrate` スクリプト
+  - GROWI `apps/app/package.json`'s `migrate` script
 - **Findings**:
-  - `migrate` スクリプトの実態: `node -r dotenv-flow/config node_modules/migrate-mongo/bin/migrate-mongo up -f config/migrate-mongo-config.js`
-  - `npm run` は内部で `sh -c` を使用するため、shell が必要
-  - 代替: スクリプトの中身を直接 node で実行すれば npm/sh は不要
-  - ただし、npm run を使用する方が保守性が高い(package.json の変更に追従可能)
+  - The actual `migrate` script content: `node -r dotenv-flow/config node_modules/migrate-mongo/bin/migrate-mongo up -f config/migrate-mongo-config.js`
+  - `npm run` internally uses `sh -c`, so a shell is required
+  - Alternative: Running the script contents directly with node eliminates the need for npm/sh
+  - However, using npm run is more maintainable (can track changes in package.json)
 - **Implications**:
-  - **最終決定**: Node.js entrypoint で `child_process.execFileSync` を使用し、migration コマンドを直接実行(npm run 不使用、シェル不要)
-  - package.json の `migrate` スクリプトの中身を entrypoint 内で直接記述する方式を採用
-  - package.json の変更時は entrypoint の更新も必要だが、DHI runtime の完全シェルレスを優先
+  - **Final decision**: Use `child_process.execFileSync` in the Node.js entrypoint to directly execute the migration command (not using npm run, no shell needed)
+  - Adopt the approach of directly writing the `migrate` script contents within the entrypoint
+  - When package.json changes, the entrypoint also needs to be updated, but priority is given to fully shell-less DHI runtime
 
-### Node.js 24 TypeScript ネイティブ実行
+### Node.js 24 Native TypeScript Execution
 
-- **Context**: entrypoint を TypeScript で記述する場合、Node.js 24 のネイティブ TypeScript 実行機能を利用可能か調査
+- **Context**: Investigation of whether Node.js 24's native TypeScript execution feature can be used when writing the entrypoint in TypeScript
 - **Sources Consulted**:
-  - [Node.js 23 Release Notes](https://nodejs.org/en/blog/release/v23.0.0) — `--experimental-strip-types` unflag
+  - [Node.js 23 Release Notes](https://nodejs.org/en/blog/release/v23.0.0) — `--experimental-strip-types` unflagged
   - [Node.js Type Stripping Documentation](https://nodejs.org/docs/latest/api/typescript.html)
 - **Findings**:
-  - Node.js 23 から type stripping がデフォルト有効(`--experimental-strip-types` フラグ不要)
-  - Node.js 24 では安定機能として利用可能
-  - **制約**: enum、namespace 等の「非 erasable syntax」は使用不可。`--experimental-transform-types` が必要
-  - interface、type alias、type annotation(`: string`、`: number` 等)は問題なく使用可能
-  - `ENTRYPOINT ["node", "docker-entrypoint.ts"]` で直接実行可能
+  - From Node.js 23, type stripping is enabled by default (no `--experimental-strip-types` flag needed)
+  - Available as a stable feature in Node.js 24
+  - **Constraint**: "Non-erasable syntax" such as enum and namespace cannot be used. `--experimental-transform-types` is required for those
+  - interface, type alias, and type annotations (`: string`, `: number`, etc.) can be used without issues
+  - Can be executed directly with `ENTRYPOINT ["node", "docker-entrypoint.ts"]`
 - **Implications**:
-  - entrypoint を TypeScript で記述し、型安全な実装が可能
-  - enum は使用せず、union type (`type Foo = 'a' | 'b'`) で代替
-  - tsconfig.json は不要(type stripping は独立動作)
+  - The entrypoint can be written in TypeScript, enabling type-safe implementation
+  - Do not use enum; use union types (`type Foo = 'a' | 'b'`) as alternatives
+  - tsconfig.json is not required (type stripping operates independently)
 
 ## Architecture Pattern Evaluation
 
 | Option | Description | Strengths | Risks / Limitations | Notes |
 |--------|-------------|-----------|---------------------|-------|
-| DHI runtime + busybox-static | busybox-static をコピーして sh/coreutils を提供 | 最小限の追加(~1MB)で全機能動作 | DHI 採用の本来の意図(攻撃面最小化)と矛盾。追加バイナリは攻撃ベクター | 却下 |
-| DHI runtime + bash/coreutils コピー | dev stage から bash と各種バイナリを個別コピー | bash の全機能が使える | 共有ライブラリ依存が複雑、コピー対象が多い | 却下 |
-| DHI dev image を runtime に使用 | dev image をそのまま本番利用 | 設定変更最小 | apt/git 等が含まれ攻撃面が増大、DHI の意味が薄れる | 却下 |
-| Node.js entrypoint(TypeScript、シェルレス) | entrypoint を TypeScript で記述。Node.js 24 のネイティブ TypeScript 実行で動作 | 完全にシェル不要、DHI runtime の攻撃面をそのまま維持、型安全 | migration コマンドを直接記述(npm run 不使用)、package.json 変更時に更新必要 | **採用** |
+| DHI runtime + busybox-static | Copy busybox-static to provide sh/coreutils | Minimal addition (~1MB) enables full functionality | Contradicts the original intent of DHI adoption (minimizing attack surface). Additional binaries are attack vectors | Rejected |
+| DHI runtime + bash/coreutils copy | Copy bash and various binaries individually from the dev stage | Full bash functionality available | Shared library dependencies are complex, many files need to be copied | Rejected |
+| DHI dev image as runtime | Use the dev image as-is for production | Minimal configuration changes | Increased attack surface due to apt/git etc., diminishes the meaning of DHI | Rejected |
+| Node.js entrypoint (TypeScript, shell-less) | Write the entrypoint in TypeScript. Runs with Node.js 24's native TypeScript execution | Completely shell-free, maintains DHI runtime's attack surface as-is, type-safe | Migration command written directly (not using npm run), updates needed when package.json changes | **Adopted** |
 
 ## Design Decisions
 
-### Decision: Node.js TypeScript entrypoint(シェル完全不要)
+### Decision: Node.js TypeScript Entrypoint (Completely Shell-Free)
 
-- **Context**: DHI runtime image にはシェルも coreutils も含まれない。busybox-static のコピーは DHI 採用の意図(攻撃面最小化)と矛盾する
+- **Context**: The DHI runtime image contains neither a shell nor coreutils. Copying busybox-static contradicts the intent of DHI adoption (minimizing attack surface)
 - **Alternatives Considered**:
-  1. busybox-static をコピーして shell + coreutils を提供 — DHI の攻撃面最小化と矛盾
-  2. bash + coreutils を個別コピー — 依存関係が複雑
-  3. Node.js TypeScript entrypoint — `fs`、`child_process`、`process.setuid/setgid` で全て完結
-- **Selected Approach**: entrypoint を TypeScript で記述(`docker-entrypoint.ts`)。Node.js 24 のネイティブ TypeScript 実行(type stripping)で直接実行
-- **Rationale**: DHI runtime に追加バイナリ一切不要。fs module でディレクトリ操作、process.setuid/setgid で権限ドロップ、execFileSync で migration、spawn でアプリ起動。型安全による保守性向上
-- **Trade-offs**: migration コマンドを直接記述(npm run 不使用)。package.json の migrate スクリプト変更時に entrypoint の更新も必要
-- **Follow-up**: Node.js 24 の type stripping が entrypoint の import 文なしの単一ファイルで正常動作することを検証
+  1. Copy busybox-static to provide shell + coreutils — Contradicts DHI's attack surface minimization
+  2. Copy bash + coreutils individually — Complex dependencies
+  3. Node.js TypeScript entrypoint — Everything can be accomplished with `fs`, `child_process`, and `process.setuid/setgid`
+- **Selected Approach**: Write the entrypoint in TypeScript (`docker-entrypoint.ts`). Execute directly using Node.js 24's native TypeScript execution (type stripping)
+- **Rationale**: No additional binaries needed in the DHI runtime whatsoever. Directory operations via fs module, privilege dropping via process.setuid/setgid, migration via execFileSync, and app startup via spawn. Improved maintainability through type safety
+- **Trade-offs**: Migration command is written directly (not using npm run). When the migrate script in package.json changes, the entrypoint also needs to be updated
+- **Follow-up**: Verify that Node.js 24's type stripping works correctly with a single-file entrypoint without import statements
 
-### Decision: Node.js ネイティブの process.setuid/setgid による権限ドロップ
+### Decision: Privilege Dropping via Node.js Native process.setuid/setgid
 
-- **Context**: gosu は DHI runtime にインストールできない。busybox-static/setpriv も不採用(追加バイナリ排除方針)
+- **Context**: gosu cannot be installed in the DHI runtime. busybox-static/setpriv are also not adopted (policy of eliminating additional binaries)
 - **Alternatives Considered**:
-  1. gosu バイナリをコピー — 動作するが、業界トレンドに逆行
-  2. setpriv バイナリをコピー — 動作するが、追加バイナリ排除方針に反する
-  3. Node.js `process.setuid/setgid` — Node.js の標準 API
-  4. Docker `--user` フラグ — entrypoint の動的処理に対応できない
-- **Selected Approach**: `process.initgroups('node', 1000)` + `process.setgid(1000)` + `process.setuid(1000)` で権限ドロップ
-- **Rationale**: 外部バイナリ完全不要。Node.js entrypoint 内で直接呼び出し可能。setgid → setuid の順序で安全に権限ドロップ
-- **Trade-offs**: entrypoint が Node.js プロセスとして root で起動し、アプリもその子プロセスとなる(gosu のような exec ではない)。ただし spawn でアプリプロセスを分離し、シグナルフォワーディングで PID 1 の責務を果たす
-- **Follow-up**: なし
-
-### Decision: turbo prune --docker パターン
-
-- **Context**: Requirement 3.1 で `COPY . .` の廃止が求められているが、`--mount=type=bind` はモノレポビルドで非実用的
+  1. Copy gosu binary — Works but goes against industry trends
+  2. Copy setpriv binary — Works but goes against the policy of eliminating additional binaries
+  3. Node.js `process.setuid/setgid` — Standard Node.js API
+  4. Docker `--user` flag — Cannot handle dynamic processing in the entrypoint
+- **Selected Approach**: Drop privileges with `process.initgroups('node', 1000)` + `process.setgid(1000)` + `process.setuid(1000)`
+- **Rationale**: No external binaries needed at all. Can be called directly within the Node.js entrypoint. Safe privilege dropping in the order setgid -> setuid
+- **Trade-offs**: The entrypoint starts as a Node.js process running as root, and the app becomes its child process (not an exec like gosu). However, the app process is separated via spawn, and signal forwarding fulfills PID 1 responsibilities
+- **Follow-up**: None
+
+### Decision: turbo prune --docker Pattern
+
+- **Context**: Requirement 3.1 requires eliminating `COPY . .`, but `--mount=type=bind` is impractical for monorepo builds
 - **Alternatives Considered**:
-  1. `--mount=type=bind` — RUN 間で永続化しないため multi-step ビルドに不向き
-  2. 単一 RUN に全ステップをまとめる — キャッシュ効率が悪い
-  3. `turbo prune --docker` — Turborepo 公式推奨
-- **Selected Approach**: `turbo prune --docker` で Docker 用にモノレポを最小化し、最適化された COPY パターンを使用
-- **Rationale**: Turborepo 公式推奨。dependency install と source copy を分離してレイヤーキャッシュを最大活用。`COPY . .` を排除しつつ実用的
-- **Trade-offs**: ビルドステージが 1 つ増える(pruner ステージ)が、キャッシュ効率の改善で相殺
-- **Follow-up**: `turbo prune --docker` の pnpm workspace 互換性を実装時に検証
+  1. `--mount=type=bind` — Does not persist across RUN instructions, unsuitable for multi-step builds
+  2. Combine all steps into a single RUN — Poor cache efficiency
+  3. `turbo prune --docker` — Officially recommended by Turborepo
+- **Selected Approach**: Use `turbo prune --docker` to minimize the monorepo for Docker, using optimized COPY patterns
+- **Rationale**: Officially recommended by Turborepo. Separates dependency install and source copy to maximize layer cache utilization. Eliminates `COPY . .` while remaining practical
+- **Trade-offs**: One additional build stage (pruner stage), but offset by improved cache efficiency
+- **Follow-up**: Verify `turbo prune --docker` compatibility with pnpm workspaces during implementation
 
-### Decision: spawn 引数によるフラグ注入
+### Decision: Flag Injection via spawn Arguments
 
-- **Context**: `--max-heap-size` は `NODE_OPTIONS` では使用不可。node コマンドの直接引数として渡す必要がある
+- **Context**: `--max-heap-size` cannot be used in `NODE_OPTIONS`. It needs to be passed as a direct argument to the node command
 - **Alternatives Considered**:
-  1. 環境変数 `GROWI_NODE_FLAGS` を export し、CMD 内の shell 変数展開で注入 — shell が必要
-  2. entrypoint 内で CMD 文字列を sed で書き換え — fragile
-  3. Node.js entrypoint で `child_process.spawn` の引数として直接渡す — シェル不要
-- **Selected Approach**: entrypoint 内でフラグ配列を組み立て、`spawn(process.execPath, [...nodeFlags, ...appArgs])` で直接渡す
-- **Rationale**: シェル変数展開不要。配列として直接渡すためシェルインジェクションのリスクゼロ。Node.js entrypoint との自然な統合
-- **Trade-offs**: CMD が不要になる(entrypoint が全ての起動処理を行う)。docker run でのコマンド上書きが entrypoint 内のロジックには影響しない
-- **Follow-up**: なし
+  1. Export environment variable `GROWI_NODE_FLAGS` and inject via shell variable expansion in CMD — Requires a shell
+  2. Rewrite CMD string with sed in the entrypoint — Fragile
+  3. Pass directly as arguments to `child_process.spawn` in the Node.js entrypoint — No shell needed
+- **Selected Approach**: Build a flag array within the entrypoint and pass it directly with `spawn(process.execPath, [...nodeFlags, ...appArgs])`
+- **Rationale**: No shell variable expansion needed. Passed directly as an array, resulting in zero risk of shell injection. Natural integration with the Node.js entrypoint
+- **Trade-offs**: CMD becomes unnecessary (the entrypoint handles all startup processing). Overriding the command with docker run does not affect the logic within the entrypoint
+- **Follow-up**: None
 
-### DHI レジストリ認証と CI/CD 統合
+### DHI Registry Authentication and CI/CD Integration
 
-- **Context**: DHI ベースイメージの pull に必要な認証方式と、既存 CodeBuild パイプラインへの統合方法を調査
+- **Context**: Investigation of the authentication method required for pulling DHI base images and how to integrate with the existing CodeBuild pipeline
 - **Sources Consulted**:
-  - [DHI How to Use an Image](https://docs.docker.com/dhi/how-to/use/) — DHI の利用手順
-  - 既存 `apps/app/docker/codebuild/buildspec.yml` — 現行の CodeBuild ビルド定義
-  - 既存 `apps/app/docker/codebuild/secretsmanager.tf` — AWS Secrets Manager 設定
+  - [DHI How to Use an Image](https://docs.docker.com/dhi/how-to/use/) — DHI usage instructions
+  - Existing `apps/app/docker/codebuild/buildspec.yml` — Current CodeBuild build definition
+  - Existing `apps/app/docker/codebuild/secretsmanager.tf` — AWS Secrets Manager configuration
 - **Findings**:
-  - DHI は Docker Hub 認証情報を使用(DHI は Docker Business/Team サブスクリプションの機能)
-  - `docker login dhi.io --username <dockerhub-user> --password-stdin` で認証可能
-  - 既存 buildspec.yml は `DOCKER_REGISTRY_PASSWORD` シークレットで docker.io にログイン済み
-  - 同じ認証情報で `dhi.io` にもログイン可能(追加シークレットは不要)
-  - CodeBuild の `reusable-app-build-image.yml` → CodeBuild Project → buildspec.yml の流れは変更不要
+  - DHI uses Docker Hub credentials (DHI is a feature of Docker Business/Team subscriptions)
+  - Authentication is possible with `docker login dhi.io --username <dockerhub-user> --password-stdin`
+  - The existing buildspec.yml is already logged into docker.io with the `DOCKER_REGISTRY_PASSWORD` secret
+  - The same credentials can be used to log into `dhi.io` as well (no additional secrets required)
+  - The flow of CodeBuild's `reusable-app-build-image.yml` -> CodeBuild Project -> buildspec.yml does not need to change
 - **Implications**:
-  - buildspec.yml の pre_build に `docker login dhi.io` を 1 行追加するだけで対応可能
-  - `secretsmanager.tf` の変更は不要
-  - Docker Hub と DHI の両方にログインが必要(docker.io は push 用、dhi.io は pull 用)
+  - Can be addressed by simply adding one line of `docker login dhi.io` to the pre_build in buildspec.yml
+  - No changes to `secretsmanager.tf` are needed
+  - Login to both Docker Hub and DHI is required (docker.io for push, dhi.io for pull)
 
-### ディレクトリ置換の影響範囲(コードベース調査)
+### Impact Scope of Directory Replacement (Codebase Investigation)
 
-- **Context**: `apps/app/docker-new/` → `apps/app/docker/` への置換時に、既存の参照が壊れないことを確認
-- **Sources Consulted**: コードベース全体を `apps/app/docker` キーワードで grep 調査
+- **Context**: Confirming that existing references will not break when replacing `apps/app/docker-new/` with `apps/app/docker/`
+- **Sources Consulted**: Grep investigation of the entire codebase with the `apps/app/docker` keyword
 - **Findings**:
-  - `buildspec.yml`: `-f ./apps/app/docker/Dockerfile` — 置換後も同一パス(変更不要)
-  - `codebuild.tf`: `buildspec = "apps/app/docker/codebuild/buildspec.yml"` — 同一(変更不要)
-  - `.github/workflows/release.yml`: `readme-filepath: ./apps/app/docker/README.md` — 同一(変更不要)
-  - `.github/workflows/ci-app.yml` / `ci-app-prod.yml`: `!apps/app/docker/**` 除外パターン — 同一(変更不要)
-  - `apps/app/bin/github-actions/update-readme.sh`: `cd docker` + sed — 同一(変更不要)
-  - Dockerfile: line 122 `apps/app/docker-new/docker-entrypoint.ts` — **要更新**(自己参照パス)
-  - `package.json` や `vitest.config` に docker 関連の参照 — なし
-  - `lefthook.yml` に docker 関連フック — なし
+  - `buildspec.yml`: `-f ./apps/app/docker/Dockerfile` — Same path after replacement (no change needed)
+  - `codebuild.tf`: `buildspec = "apps/app/docker/codebuild/buildspec.yml"` — Same (no change needed)
+  - `.github/workflows/release.yml`: `readme-filepath: ./apps/app/docker/README.md` — Same (no change needed)
+  - `.github/workflows/ci-app.yml` / `ci-app-prod.yml`: `!apps/app/docker/**` exclusion pattern — Same (no change needed)
+  - `apps/app/bin/github-actions/update-readme.sh`: `cd docker` + sed — Same (no change needed)
+  - Within Dockerfile: line 122 `apps/app/docker-new/docker-entrypoint.ts` — **Needs updating** (self-referencing path)
+  - `package.json` and `vitest.config` for docker-related references — None
+  - `lefthook.yml` for docker-related hooks — None
 - **Implications**:
-  - 置換時に更新が必要なのは Dockerfile 内の自己参照パス 1 箇所のみ
-  - 外部参照(CI/CD、GitHub Actions)は全て `apps/app/docker/` パスを使用しており変更不要
-  - `codebuild/` ディレクトリと `README.md` は `docker/` 内にそのまま維持
+  - Only one location within the Dockerfile (self-referencing path) needs to be updated during replacement
+  - All external references (CI/CD, GitHub Actions) already use the `apps/app/docker/` path and require no changes
+  - The `codebuild/` directory and `README.md` are maintained as-is within `docker/`
 
 ## Risks & Mitigations
 
-- **Node.js 24 TypeScript ネイティブ実行の安定性**: type stripping は Node.js 23 で unflag 済み。Node.js 24 では安定機能。ただし enum 等の非 erasable syntax は使用不可 → interface/type のみ使用
-- **migration コマンドの直接記述**: package.json の `migrate` スクリプトを entrypoint 内に直接記述するため、変更時に同期が必要 → 実装時にコメントで明記
-- **turbo prune の pnpm workspace 互換性**: 実装時に検証。非互換の場合は最適化された COPY パターンにフォールバック
-- **process.setuid/setgid の制限**: supplementary groups の初期化に `process.initgroups` が必要。setgid → setuid の順序厳守
-- **DHI イメージの docker login 要件**: CI/CD で `docker login dhi.io` が必要。認証情報管理のセキュリティ考慮が必要
+- **Stability of Node.js 24 native TypeScript execution**: Type stripping was unflagged in Node.js 23. It is a stable feature in Node.js 24. However, non-erasable syntax such as enum cannot be used -> Use only interface/type
+- **Direct description of migration command**: The `migrate` script from package.json is written directly in the entrypoint, so synchronization is needed when changes occur -> Clearly noted in comments during implementation
+- **turbo prune compatibility with pnpm workspaces**: Verify during implementation. If incompatible, fall back to an optimized COPY pattern
+- **Limitations of process.setuid/setgid**: `process.initgroups` is required for supplementary group initialization. The order setgid -> setuid must be strictly followed
+- **docker login requirement for DHI images**: `docker login dhi.io` is required in CI/CD. Security considerations for credential management are needed
 
 ## References
 
-- [Docker Hardened Images Documentation](https://docs.docker.com/dhi/) — DHI の全体像と利用方法
-- [DHI Catalog GitHub](https://github.com/docker-hardened-images/catalog) — イメージ定義とタグ一覧
-- [Turborepo Docker Guide](https://turbo.build/repo/docs/handbook/deploying-with-docker) — turbo prune --docker パターン
-- [pnpm Docker Documentation](https://pnpm.io/docker) — pnpm のDockerビルド推奨
-- [Future Architect: 2024年版 Dockerfile ベストプラクティス](https://future-architect.github.io/articles/20240726a/) — モダンな Dockerfile 構文
-- [MongoDB Docker: gosu → setpriv](https://github.com/docker-library/mongo/pull/714) — setpriv 移行の先行事例
-- [Docker Healthchecks in Distroless](https://www.mattknight.io/blog/docker-healthchecks-in-distroless-node-js) — curl なしのヘルスチェック
-- GROWI メモリ使用量調査レポート (`apps/app/tmp/memory-results/REPORT.md`) — ヒープサイズ制御の根拠
+- [Docker Hardened Images Documentation](https://docs.docker.com/dhi/) — Overview and usage of DHI
+- [DHI Catalog GitHub](https://github.com/docker-hardened-images/catalog) — Image definitions and tag list
+- [Turborepo Docker Guide](https://turbo.build/repo/docs/handbook/deploying-with-docker) — turbo prune --docker pattern
+- [pnpm Docker Documentation](https://pnpm.io/docker) — pnpm Docker build recommendations
+- [Future Architect: 2024 Edition Dockerfile Best Practices](https://future-architect.github.io/articles/20240726a/) — Modern Dockerfile syntax
+- [MongoDB Docker: gosu -> setpriv](https://github.com/docker-library/mongo/pull/714) — Precedent for setpriv migration
+- [Docker Healthchecks in Distroless](https://www.mattknight.io/blog/docker-healthchecks-in-distroless-node-js) — Health checks without curl
+- GROWI memory usage investigation report (`apps/app/tmp/memory-results/REPORT.md`) — Basis for heap size control

+ 1 - 1
.kiro/specs/official-docker-image/spec.json

@@ -2,7 +2,7 @@
   "feature_name": "official-docker-image",
   "created_at": "2026-02-20T00:00:00.000Z",
   "updated_at": "2026-02-24T00:15:00.000Z",
-  "language": "ja",
+  "language": "en",
   "phase": "implemented",
   "approvals": {
     "requirements": {

+ 133 - 133
.kiro/specs/official-docker-image/tasks.md

@@ -1,196 +1,196 @@
 # Implementation Plan
 
-> **タスク順序の設計方針**:
-> - **Phase 1(本フェーズ)**: DHI ベースイメージ + TypeScript entrypoint で、現行と同一仕様のイメージを再現する。ビルドパイプライン(`COPY . .` による 3 ステージ構成)は現行を維持し、**runtime の安全な移行を優先**する。
-> - **Phase 2(次フェーズ)**: `turbo prune --docker` パターンの導入によるビルド最適化。Phase 1 で runtime が安定してから実施する。pruner/deps ステージの追加で 5 ステージ化。
+> **Task ordering design policy**:
+> - **Phase 1 (this phase)**: Reproduce an image with the same specifications as the current one using a DHI base image + TypeScript entrypoint. The build pipeline (3-stage structure using `COPY . .`) is kept as-is, **prioritizing a safe runtime migration**.
+> - **Phase 2 (next phase)**: Introduction of build optimization via the `turbo prune --docker` pattern. This will be done after runtime is stable in Phase 1. Adding pruner/deps stages to create a 5-stage structure.
 >
-> **実装ディレクトリ**: `apps/app/docker-new/` に新規作成する。現行の `apps/app/docker/` は一切変更しない。並行して比較・検証可能な状態を維持する。
+> **Implementation directory**: Create new files in `apps/app/docker-new/`. The existing `apps/app/docker/` will not be modified at all. Maintain a state where parallel comparison and verification is possible.
 >
-> ディレクトリ権限周りは最優先で実装・テストし、デグレを早期に検出する。entrypoint(TypeScript)と Dockerfile は独立したファイルのため、一部タスクは並行実行可能。
+> Directory permission handling is implemented and tested as the highest priority to detect regressions early. Since the entrypoint (TypeScript) and Dockerfile are independent files, some tasks can be executed in parallel.
 
-## Phase 1: DHI + TypeScript entrypoint(現行ビルドパターン維持)
+## Phase 1: DHI + TypeScript entrypoint (maintaining current build pattern)
 
-- [x] 1. (P) ビルドコンテキストフィルタの強化
-  - 現行の除外ルールに `.git`、`.env*`(production 以外)、テストファイル、IDE 設定ファイル等を追加する
-  - セキュリティ上の機密ファイル(シークレット、認証情報)がコンテキストに含まれないことを確認する
-  - 現行の除外ルール(`node_modules`、`.next`、`.turbo`、`apps/slackbot-proxy` 等)は維持する
+- [x] 1. (P) Strengthen build context filter
+  - Add `.git`, `.env*` (except production), test files, IDE configuration files, etc. to the current exclusion rules
+  - Verify that security-sensitive files (secrets, credentials) are not included in the context
+  - Maintain the current exclusion rules (`node_modules`, `.next`, `.turbo`, `apps/slackbot-proxy`, etc.)
   - _Requirements: 4.3_
 
-- [x] 2. TypeScript entrypoint のディレクトリ初期化と権限管理
-- [x] 2.1 (P) entrypoint スケルトンと再帰 chown ヘルパーの作成
-  - Node.js 24 の type stripping で直接実行可能な TypeScript ファイルを新規作成する(enum 不使用、erasable syntax のみ)
-  - メインの実行フローを `main()` 関数として構造化し、エラーハンドリングのトップレベル try-catch を設ける
-  - ディレクトリ内のファイル・サブディレクトリを再帰的に所有者変更するヘルパー関数を実装する
-  - ヘルパー関数のユニットテストを作成する(ネストされたディレクトリ構造での再帰動作を検証)
+- [x] 2. TypeScript entrypoint directory initialization and permission management
+- [x] 2.1 (P) Create entrypoint skeleton and recursive chown helper
+  - Create a new TypeScript file that can be directly executed with Node.js 24 type stripping (no enums, erasable syntax only)
+  - Structure the main execution flow as a `main()` function with top-level try-catch for error handling
+  - Implement a helper function that recursively changes ownership of files and subdirectories within a directory
+  - Create unit tests for the helper function (verify recursive behavior with nested directory structures)
   - _Requirements: 6.8_
 
-- [x] 2.2 ディレクトリ初期化処理の実装
-  - `/data/uploads` の作成、`./public/uploads` へのシンボリックリンク作成、再帰的な所有者変更を実装する
-  - `/tmp/page-bulk-export` の作成、再帰的な所有者変更、パーミッション 700 の設定を実装する
-  - 冪等性を確保する(`recursive: true` による mkdir、既存シンボリックリンクの重複作成防止)
-  - **現行 `docker-entrypoint.sh` と同一の振る舞い**を保証するユニットテストを作成する(fs モック使用、ディレクトリ・シンボリックリンク・所有者・パーミッションの各状態を検証)
-  - 失敗時(ボリュームマウント未設定等)にプロセス終了(exit code 1)することを検証する
+- [x] 2.2 Implement directory initialization processing
+  - Implement creation of `/data/uploads`, symlink creation to `./public/uploads`, and recursive ownership change
+  - Implement creation of `/tmp/page-bulk-export`, recursive ownership change, and permission 700 setting
+  - Ensure idempotency (`recursive: true` for mkdir, prevent duplicate symlink creation)
+  - Create unit tests that **guarantee the same behavior as the current `docker-entrypoint.sh`** (using fs mocks, verifying each state of directories, symlinks, ownership, and permissions)
+  - Verify that the process exits (exit code 1) on failure (e.g., volume mount not configured)
   - _Requirements: 6.3, 6.4_
 
-- [x] 2.3 権限ドロップの実装
-  - root から node ユーザー(UID 1000, GID 1000)への降格処理を実装する
-  - supplementary groups の初期化を行い、setgid → setuid の順序を厳守する(逆順だと setgid が失敗する)
-  - 権限ドロップ失敗時にエラーメッセージを出力してプロセスを終了する
+- [x] 2.3 Implement privilege dropping
+  - Implement demotion from root to node user (UID 1000, GID 1000)
+  - Initialize supplementary groups, strictly following the order of setgid then setuid (reverse order causes setgid to fail)
+  - Output an error message and exit the process on privilege drop failure
   - _Requirements: 4.1, 6.2_
 
-- [x] 3. ヒープサイズ算出とノードフラグ組み立て
-- [x] 3.1 (P) cgroup メモリリミット検出の実装
-  - cgroup v2 ファイルの読み取りと数値パースを実装する(`"max"` 文字列は unlimited として扱う)
-  - cgroup v1 ファイルへのフォールバックを実装する(64GB 超は unlimited として扱う)
-  - メモリリミットの 60% をヒープサイズ(MB 単位)として算出する
-  - ファイル読み取り失敗時は警告ログを出力し、フラグなし(V8 デフォルト)で続行する
-  - 各パターン(v2 正常検出、v2 unlimited、v1 フォールバック、v1 unlimited、検出不可)のユニットテストを作成する
+- [x] 3. Heap size calculation and node flag assembly
+- [x] 3.1 (P) Implement cgroup memory limit detection
+  - Implement reading and numeric parsing of cgroup v2 files (treat the `"max"` string as unlimited)
+  - Implement fallback to cgroup v1 files (treat values exceeding 64GB as unlimited)
+  - Calculate 60% of the memory limit as the heap size (in MB)
+  - On file read failure, output a warning log and continue without flags (V8 default)
+  - Create unit tests for each pattern (v2 normal detection, v2 unlimited, v1 fallback, v1 unlimited, detection unavailable)
   - _Requirements: 2.2, 2.3_
 
-- [x] 3.2 (P) 環境変数によるヒープサイズ指定の実装
-  - `GROWI_HEAP_SIZE` 環境変数のパースとバリデーションを実装する(正の整数、MB 単位)
-  - 不正値(NaN、負数、空文字列)の場合は警告ログを出力してフラグなしにフォールバックする
-  - 環境変数指定が cgroup 自動算出より優先されることをテストで確認する
+- [x] 3.2 (P) Implement heap size specification via environment variable
+  - Implement parsing and validation of the `GROWI_HEAP_SIZE` environment variable (positive integer, in MB)
+  - On invalid values (NaN, negative numbers, empty string), output a warning log and fall back to no flags
+  - Confirm via tests that the environment variable takes priority over cgroup auto-calculation
   - _Requirements: 2.1_
 
-- [x] 3.3 ノードフラグの組み立てとログ出力の実装
-  - 3 段フォールバック(環境変数 → cgroup 算出 → V8 デフォルト)の統合ロジックを実装する
-  - `--expose_gc` フラグを常時付与する
-  - `GROWI_OPTIMIZE_MEMORY=true` で `--optimize-for-size`、`GROWI_LITE_MODE=true` で `--lite-mode` を追加する
-  - `--max-heap-size` を spawn 引数として直接渡す構造にする(`--max_old_space_size` は不使用、`NODE_OPTIONS` には含めない)
-  - 適用されたフラグの内容を標準出力にログ出力する(どの段で決定されたかを含む)
-  - 環境変数の各組み合わせパターン(全未設定、HEAP_SIZE のみ、全有効等)のユニットテストを作成する
+- [x] 3.3 Implement node flag assembly and log output
+  - Implement the 3-tier fallback integration logic (environment variable -> cgroup calculation -> V8 default)
+  - Always include the `--expose_gc` flag
+  - Add `--optimize-for-size` when `GROWI_OPTIMIZE_MEMORY=true`, and `--lite-mode` when `GROWI_LITE_MODE=true`
+  - Pass `--max-heap-size` directly as a spawn argument (do not use `--max_old_space_size`, do not include in `NODE_OPTIONS`)
+  - Log the applied flags to stdout (including which tier determined the value)
+  - Create unit tests for each combination of environment variables (all unset, HEAP_SIZE only, all enabled, etc.)
   - _Requirements: 2.4, 2.5, 2.6, 2.7, 6.1, 6.6, 6.7_
 
-- [x] 4. マイグレーション実行とアプリプロセス管理
-- [x] 4.1 マイグレーションの直接実行
-  - node バイナリを直接呼び出して migrate-mongo を実行する(npm run を使用しない、シェルを介さない)
-  - 標準入出力を inherit して migration のログを表示する
-  - migration 失敗時は例外をキャッチしてプロセスを終了し、コンテナオーケストレーターによる再起動を促す
+- [x] 4. Migration execution and app process management
+- [x] 4.1 Direct migration execution
+  - Execute migrate-mongo by directly calling the node binary (do not use npm run, do not go through a shell)
+  - Inherit stdio to display migration logs
+  - On migration failure, catch the exception and exit the process, prompting restart by the container orchestrator
   - _Requirements: 6.5_
 
-- [x] 4.2 アプリプロセスの起動とシグナル管理
-  - 算出済みノードフラグを引数に含めた子プロセスとしてアプリケーションを起動する
-  - SIGTERM、SIGINT、SIGHUP を子プロセスにフォワードする
-  - 子プロセスの終了コード(またはシグナル)を entrypoint の終了コードとして伝播する
-  - PID 1 としての責務(シグナルフォワーディング、子プロセス reap、graceful shutdown)を検証するテストを作成する
+- [x] 4.2 App process startup and signal management
+  - Start the application as a child process with the calculated node flags included in the arguments
+  - Forward SIGTERM, SIGINT, and SIGHUP to the child process
+  - Propagate the child process exit code (or signal) as the entrypoint exit code
+  - Create tests to verify PID 1 responsibilities (signal forwarding, child process reaping, graceful shutdown)
   - _Requirements: 6.2, 6.5_
 
-- [x] 5. Dockerfile の再構築(現行 3 ステージパターン + DHI)
-- [x] 5.1 (P) base ステージの構築
-  - DHI dev イメージをベースに設定し、syntax ディレクティブを最新安定版自動追従に更新する
-  - wget スタンドアロンスクリプトで pnpm をインストールする(バージョンのハードコードを排除する)
-  - turbo をグローバルにインストールする
-  - ビルドに必要なパッケージを `--no-install-recommends` 付きでインストールし、apt キャッシュマウントを適用する
+- [x] 5. Dockerfile reconstruction (current 3-stage pattern + DHI)
+- [x] 5.1 (P) Build the base stage
+  - Set the DHI dev image as the base and update the syntax directive to auto-follow the latest stable version
+  - Install pnpm via wget standalone script (eliminate hardcoded versions)
+  - Install turbo globally
+  - Install packages required for building with `--no-install-recommends` and apply apt cache mounts
   - _Requirements: 1.1, 1.2, 1.3, 1.5, 3.3, 4.4_
 
-- [x] 5.2 builder ステージの構築
-  - 現行の `COPY . .` パターンを維持してモノレポ全体をコピーし、依存インストール・ビルド・本番依存抽出を行う
-  - `--frozen-lockfile` の typo(ダッシュ3つ → 2つ)を修正する
-  - pnpm store のキャッシュマウントを設定してリビルド時間を短縮する
-  - 本番依存のみを抽出し、tar.gz にパッケージングする(`apps/app/tmp` ディレクトリを含む)
-  - `.next/cache` がアーティファクトに含まれないことを保証する
+- [x] 5.2 Build the builder stage
+  - Maintain the current `COPY . .` pattern to copy the entire monorepo, then install dependencies, build, and extract production dependencies
+  - Fix the `--frozen-lockfile` typo (3 dashes -> 2 dashes)
+  - Configure pnpm store cache mounts to reduce rebuild time
+  - Extract only production dependencies and package them into tar.gz (including the `apps/app/tmp` directory)
+  - Guarantee that `.next/cache` is not included in the artifact
   - _Requirements: 1.4, 3.2, 3.4_
 
-- [x] 5.3 release ステージの構築
-  - DHI ランタイムイメージをベースに設定し、追加バイナリのコピーを一切行わない
-  - ビルドステージのアーティファクトをバインドマウント経由で展開する
-  - TypeScript entrypoint ファイルを COPY し、ENTRYPOINT に node 経由の直接実行を設定する
-  - リリースステージにビルドツール(turbo、pnpm、node-gyp 等)やビルド用パッケージ(wget、curl 等)が含まれないことを確認する
+- [x] 5.3 Build the release stage
+  - Set the DHI runtime image as the base with no additional binary copying
+  - Extract build stage artifacts via bind mount
+  - COPY the TypeScript entrypoint file and set ENTRYPOINT to direct execution via node
+  - Verify that build tools (turbo, pnpm, node-gyp, etc.) and build packages (wget, curl, etc.) are not included in the release stage
   - _Requirements: 1.1, 3.5, 4.2, 4.5_
 
-- [x] 5.4 (P) OCI ラベルとポート・ボリューム宣言の設定
-  - OCI 標準ラベル(source、title、description、vendor)を設定する
-  - `EXPOSE 3000` と `VOLUME /data` を維持する
+- [x] 5.4 (P) Configure OCI labels and port/volume declarations
+  - Set OCI standard labels (source, title, description, vendor)
+  - Maintain `EXPOSE 3000` and `VOLUME /data`
   - _Requirements: 5.1, 5.2, 5.3_
 
-- [x] 6. 統合検証と後方互換性の確認
-- [x] 6.1 Docker ビルドの E2E 検証
-  - 3 ステージ全てが正常完了する Docker ビルドを実行し、ビルドエラーがないことを確認する
-  - リリースイメージにシェル、apt、ビルドツールが含まれていないことを確認する
+- [x] 6. Integration verification and backward compatibility confirmation
+- [x] 6.1 Docker build E2E verification
+  - Execute a Docker build where all 3 stages complete successfully and confirm there are no build errors
+  - Verify that the release image does not contain a shell, apt, or build tools
   - _Requirements: 1.1, 4.2, 4.5_
 
-- [x] 6.2 ランタイム動作と後方互換性の検証
-  - 環境変数(`MONGO_URI`、`FILE_UPLOAD` 等)が従来通りアプリケーションに透過されることを確認する
-  - `/data` ボリュームマウントとの互換性およびファイルアップロード動作を確認する
-  - ポート 3000 でのリッスン動作を確認する
-  - メモリ管理環境変数が未設定の場合に V8 デフォルト動作となることを確認する
-  - `docker compose up` での起動と SIGTERM による graceful shutdown を確認する
+- [x] 6.2 Runtime behavior and backward compatibility verification
+  - Verify that environment variables (`MONGO_URI`, `FILE_UPLOAD`, etc.) are transparently passed to the application as before
+  - Verify compatibility with `/data` volume mounts and file upload functionality
+  - Verify listening on port 3000
+  - Verify that V8 default behavior is used when memory management environment variables are not set
+  - Verify startup with `docker compose up` and graceful shutdown via SIGTERM
   - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5_
 
 ## Known Issues
 
-- [ ] `process.initgroups()` による supplementary groups 初期化の追加
-  - design.md では `process.initgroups('node', 1000)` を呼ぶ設計だが、`@types/node` に型定義が存在しないため Phase 1 では実装を見送った
-  - ランタイムには `process.initgroups` は存在する(Node.js 24 で確認済み)
-  - 対応方法: `@types/node` の修正を待つか、`(process as any).initgroups('node', 1000)` で回避
-  - 実用上の影響は低い(Docker コンテナ内の node ユーザーは通常 supplementary groups を持たない)
+- [ ] Addition of supplementary groups initialization via `process.initgroups()`
+  - The design in design.md calls for `process.initgroups('node', 1000)`, but implementation was deferred in Phase 1 because the type definition does not exist in `@types/node`
+  - `process.initgroups` does exist at runtime (confirmed in Node.js 24)
+  - Workaround options: Wait for the `@types/node` fix, or use `(process as any).initgroups('node', 1000)` as a workaround
+  - Practical impact is low (the node user in a Docker container typically has no supplementary groups)
   - _Requirements: 4.1, 6.2_
 
-## Design からの意図的な逸脱(Phase 1 E2E 検証で発覚・対応済み)
+## Intentional deviations from Design (discovered and addressed during Phase 1 E2E verification)
 
-### DHI dev イメージの最小構成への対応
+### Adaptation to DHI dev image minimal configuration
 
-DHI dev イメージ (`dhi.io/node:24-debian13-dev`) は想定より最小構成であり、`which` コマンドが未同梱だった。以下の修正を実施済み:
+The DHI dev image (`dhi.io/node:24-debian13-dev`) was more minimal than expected, and the `which` command was not included. The following fix was applied:
 
-1. **pnpm インストール**: `SHELL="$(which sh)"` → `SHELL=/bin/sh` に変更(`which` コマンド不在のため)
+1. **pnpm installation**: Changed from `SHELL="$(which sh)"` to `SHELL=/bin/sh` (due to absence of the `which` command)
 
-### DHI runtime イメージのシェル完全不在への対応
+### Adaptation to complete absence of shell in DHI runtime image
 
-DHI runtime イメージ (`dhi.io/node:24-debian13`) には `/bin/sh` が存在しなかった。Design では `--mount=type=bind,from=builder` + `RUN tar -zxf` でアーティファクトを展開する設計だったが、`RUN` 命令は `/bin/sh` を必要とするため実行不可。
+The DHI runtime image (`dhi.io/node:24-debian13`) did not have `/bin/sh`. The Design planned to extract artifacts using `--mount=type=bind,from=builder` + `RUN tar -zxf`, but `RUN` instructions require `/bin/sh` and thus could not be executed.
 
-**対応**:
-- **builder ステージ**: `tar -zcf` → ステージングディレクトリ `/tmp/release/` に `cp -a` でコピー
-- **release ステージ**: `RUN --mount=type=bind... tar -zxf` → `COPY --from=builder --chown=node:node` に変更
-- `COPY`, `WORKDIR`, `ENV`, `LABEL`, `ENTRYPOINT` はすべて Docker デーモンが直接処理するためシェル不要
+**Resolution**:
+- **builder stage**: Changed from `tar -zcf` to copying with `cp -a` into a staging directory `/tmp/release/`
+- **release stage**: Changed from `RUN --mount=type=bind... tar -zxf` to `COPY --from=builder --chown=node:node`
+- `COPY`, `WORKDIR`, `ENV`, `LABEL`, `ENTRYPOINT` are all processed directly by the Docker daemon and do not require a shell
 
-**影響**: Design の Req 3.5(`--mount=type=bind,from=builder` パターン)は `COPY --from=builder` パターンに代替。runtime にシェルが不要という Design のセキュリティ目標(Req 4.2, 4.5)はより強固に達成された。
+**Impact**: Design Req 3.5 (`--mount=type=bind,from=builder` pattern) was replaced with the `COPY --from=builder` pattern. The Design's security goal of not requiring a shell at runtime (Req 4.2, 4.5) was achieved even more robustly.
 
-## Phase 2: turbo prune --docker ビルド最適化(次フェーズ)
+## Phase 2: turbo prune --docker build optimization (next phase)
 
-> Phase 1 で runtime が安定した後に実施する。現行の `COPY . .` + 3 ステージ構成を `turbo prune --docker` + 5 ステージ構成に移行し、ビルドキャッシュ効率を向上させる。
+> To be done after runtime is stable in Phase 1. Migrate from the current `COPY . .` + 3-stage structure to a `turbo prune --docker` + 5-stage structure to improve build cache efficiency.
 
-- [x] 7. turbo prune --docker パターンの導入
-- [x] 7.1 pruner ステージの新設
-  - base ステージの直後に pruner ステージを追加し、`turbo prune @growi/app @growi/pdf-converter --docker` でモノレポを Docker 用に最小化する
-  - `@growi/pdf-converter` を含める理由: `@growi/pdf-converter-client/turbo.json` が `@growi/pdf-converter#gen:swagger-spec` タスク依存を持つため、pruned workspace に含めないと turbo がタスク依存を解決できない
-  - pnpm workspace との互換性を検証済み(18 パッケージが正しく出力される)
-  - 出力(json ディレクトリ、lockfile、full ディレクトリ)が正しく生成されることを確認済み
+- [x] 7. Introduction of turbo prune --docker pattern
+- [x] 7.1 Create pruner stage
+  - Add a pruner stage immediately after the base stage, minimizing the monorepo for Docker with `turbo prune @growi/app @growi/pdf-converter --docker`
+  - Reason for including `@growi/pdf-converter`: `@growi/pdf-converter-client/turbo.json` has a task dependency on `@growi/pdf-converter#gen:swagger-spec`, so turbo cannot resolve task dependencies unless it is included in the pruned workspace
+  - Verified compatibility with pnpm workspace (18 packages are correctly output)
+  - Confirmed that the output (json directory, lockfile, full directory) is generated correctly
   - _Requirements: 3.1_
 
-- [x] 7.2 deps ステージの分離と builder の再構成
-  - builder ステージから依存インストールを分離し、deps ステージとして独立させる
-  - pruner の出力から package.json 群と lockfile のみをコピーして依存をインストールする(レイヤーキャッシュ効率化)
-  - builder ステージは deps をベースにソースコードをコピーしてビルドのみを行う構成に変更する
-  - 依存変更なし・ソースコードのみ変更の場合に、依存インストールレイヤーがキャッシュされることを検証する
+- [x] 7.2 Separate deps stage and restructure builder
+  - Separate dependency installation from the builder stage into an independent deps stage
+  - Copy only the package.json files and lockfile from pruner output to install dependencies (layer cache optimization)
+  - Change the builder stage to a structure that uses deps as a base and only copies source code and builds
+  - Verify that the dependency installation layer is cached when there are no dependency changes and only source code changes
   - _Requirements: 3.1, 3.2_
 
-- [x] 7.3 5 ステージ構成の統合検証
-  - base → pruner → deps → builder → release の 5 ステージ全てが正常完了することを確認する
-  - Phase 1 の 3 ステージ構成と同等の runtime 動作を維持していることを確認する
-  - ビルドキャッシュの効率改善(ソースコード変更時に依存インストールがスキップされること)を検証する
+- [x] 7.3 Integration verification of 5-stage structure
+  - Confirm that all 5 stages (base -> pruner -> deps -> builder -> release) complete successfully
+  - Confirm that the same runtime behavior as the Phase 1 3-stage structure is maintained
+  - Verify improvement in build cache efficiency (dependency installation is skipped when only source code changes)
   - _Requirements: 3.1, 3.2, 3.4_
 
-## Phase 3: 本番置換と CI/CD 対応
+## Phase 3: Production replacement and CI/CD support
 
-> Phase 2 で 5 ステージ構成が安定した後に実施する。`apps/app/docker-new/` の成果物を `apps/app/docker/` に移動して旧ファイルを削除し、CI/CD パイプラインを DHI 対応に更新する。
+> To be done after the 5-stage structure is stable in Phase 2. Move the artifacts from `apps/app/docker-new/` to `apps/app/docker/`, delete the old files, and update the CI/CD pipeline for DHI support.
 
-- [x] 8. 本番置換と CI/CD 対応
-- [x] 8.1 (P) docker-new ディレクトリから docker ディレクトリへの置換
-  - `apps/app/docker/` 内の旧ファイル(旧 `Dockerfile`、`docker-entrypoint.sh`、旧 `Dockerfile.dockerignore`)を削除する
-  - `apps/app/docker-new/` 内の全ファイル(`Dockerfile`、`docker-entrypoint.ts`、`docker-entrypoint.spec.ts`、`Dockerfile.dockerignore`)を `apps/app/docker/` に移動する
-  - `apps/app/docker-new/` ディレクトリを削除する
-  - `codebuild/` ディレクトリと `README.md` が `apps/app/docker/` 内に維持されていることを確認する
-  - Dockerfile 内の entrypoint コピーパス(`apps/app/docker-new/docker-entrypoint.ts`)を `apps/app/docker/docker-entrypoint.ts` に更新する
+- [x] 8. Production replacement and CI/CD support
+- [x] 8.1 (P) Replace docker-new directory with docker directory
+  - Delete old files in `apps/app/docker/` (old `Dockerfile`, `docker-entrypoint.sh`, old `Dockerfile.dockerignore`)
+  - Move all files in `apps/app/docker-new/` (`Dockerfile`, `docker-entrypoint.ts`, `docker-entrypoint.spec.ts`, `Dockerfile.dockerignore`) to `apps/app/docker/`
+  - Delete the `apps/app/docker-new/` directory
+  - Confirm that the `codebuild/` directory and `README.md` are maintained within `apps/app/docker/`
+  - Update the entrypoint copy path in the Dockerfile (from `apps/app/docker-new/docker-entrypoint.ts` to `apps/app/docker/docker-entrypoint.ts`)
   - _Requirements: 8.1, 8.2_
 
-- [x] 8.2 (P) buildspec.yml の DHI レジストリログイン追加
-  - `apps/app/docker/codebuild/buildspec.yml` の pre_build フェーズに `docker login dhi.io` コマンドを追加する
-  - DHI は Docker Hub 認証情報を使用するため、既存の `DOCKER_REGISTRY_PASSWORD` シークレットと `growimoogle` ユーザー名を再利用する
-  - buildspec.yml の Dockerfile パス(`./apps/app/docker/Dockerfile`)が置換後も正しいことを確認する
+- [x] 8.2 (P) Add DHI registry login to buildspec.yml
+  - Add a `docker login dhi.io` command to the pre_build phase of `apps/app/docker/codebuild/buildspec.yml`
+  - DHI uses Docker Hub credentials, so reuse the existing `DOCKER_REGISTRY_PASSWORD` secret and `growimoogle` username
+  - Confirm that the Dockerfile path in buildspec.yml (`./apps/app/docker/Dockerfile`) is correct after replacement
   - _Requirements: 8.3, 8.4_
 
-- [x] 8.3 置換後の統合検証
-  - 置換後の `apps/app/docker/Dockerfile` で Docker ビルドが正常完了することを確認する
-  - 既存の外部参照(`codebuild.tf`、`.github/workflows/release.yml`、`ci-app.yml`、`update-readme.sh`)が正しく動作することを確認する
+- [x] 8.3 Integration verification after replacement
+  - Confirm that Docker build completes successfully with the replaced `apps/app/docker/Dockerfile`
+  - Confirm that existing external references (`codebuild.tf`, `.github/workflows/release.yml`, `ci-app.yml`, `update-readme.sh`) work correctly
   - _Requirements: 8.1, 8.2, 8.3, 8.4_