start-vibing-stacks 2.14.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/stacks/_shared/commands/feature.md +21 -9
- package/stacks/_shared/commands/fix.md +20 -6
- package/stacks/_shared/commands/research.md +18 -7
- package/stacks/_shared/commands/validate.md +31 -4
- package/stacks/_shared/skills/codebase-knowledge/SKILL.md +46 -49
- package/stacks/_shared/skills/codebase-knowledge/TEMPLATE.md +35 -14
- package/stacks/_shared/skills/docker-patterns/SKILL.md +184 -31
- package/stacks/_shared/skills/docs-tracker/SKILL.md +83 -35
- package/stacks/_shared/skills/git-workflow/SKILL.md +140 -17
- package/stacks/_shared/skills/hook-development/SKILL.md +230 -52
- package/stacks/_shared/skills/observability/SKILL.md +72 -2
- package/stacks/_shared/skills/openapi-design/SKILL.md +111 -3
- package/stacks/_shared/skills/playwright-automation/SKILL.md +173 -27
- package/stacks/_shared/skills/postgres-patterns/SKILL.md +85 -4
- package/stacks/_shared/skills/research-cache/SKILL.md +112 -35
- package/stacks/_shared/skills/secrets-management/SKILL.md +98 -12
- package/stacks/_shared/skills/security-baseline/SKILL.md +115 -18
- package/stacks/_shared/skills/ui-ux-audit/SKILL.md +94 -35
package/package.json
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature
|
|
3
|
+
description: Start a new feature with the full workflow (research → plan → implement → test → security → quality → commit → docs).
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# /feature — Start New Feature
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
Always read `.claude/config/active-project.json` first to know the active stack.
|
|
10
|
+
|
|
11
|
+
Execute in order — do not skip steps. Steps 5–6 have **VETO power** over commit.
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
| # | Step | Agent / Skill |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| 1 | **Research** (only for new tech / patterns) | `research-web` |
|
|
16
|
+
| 2 | **Plan** — break into tasks, create TODO list | (you, in plan mode) |
|
|
17
|
+
| 3 | **Implement** — follow stack patterns + strict types | (you) |
|
|
18
|
+
| 4 | **Test** — unit + e2e | `tester` |
|
|
19
|
+
| 5 | **Security** — adversarial audit | `security-auditor` (VETO) |
|
|
20
|
+
| 6 | **Quality** — typecheck → lint → test → build | `quality-gate` |
|
|
21
|
+
| 7 | **Commit** — gate-aware, diff-driven message | `commit-manager` |
|
|
22
|
+
| 8 | **Map** — files + commits → domains | `documenter` |
|
|
23
|
+
| 9 | **Wisdom + Last Change** — record learnings, refresh `CLAUDE.md` | `domain-updater` |
|
|
12
24
|
|
|
13
|
-
|
|
25
|
+
If `security-auditor` returns CRITICAL/HIGH/MEDIUM, fix before re-running step 5. Steps 8–9 run AFTER push and never before.
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fix
|
|
3
|
+
description: Fix a bug — reproduce, isolate, minimal fix, regression test, security check, commit, record wisdom.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# /fix — Fix Bug
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
Always read `.claude/config/active-project.json` first.
|
|
10
|
+
|
|
11
|
+
| # | Step | Agent / Skill |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| 1 | **Reproduce** — read the error, reproduce locally, isolate the root cause (NOT the symptom) | (you) |
|
|
14
|
+
| 2 | **Failing test first** — write a regression test that proves the bug | `tester` |
|
|
15
|
+
| 3 | **Apply minimal fix** — do not refactor adjacent code in the same commit | (you) |
|
|
16
|
+
| 4 | **Verify** — re-run the regression test + the wider suite | `tester` |
|
|
17
|
+
| 5 | **Security** — only if the bug touched auth / input / secrets / SSRF surface | `security-auditor` (VETO) |
|
|
18
|
+
| 6 | **Quality gate** — typecheck → lint → test → build | `quality-gate` |
|
|
19
|
+
| 7 | **Commit** — `fix(scope): <subject>`, diff-driven body | `commit-manager` |
|
|
20
|
+
| 8 | **Map** — files + commit → domain | `documenter` |
|
|
21
|
+
| 9 | **Wisdom** — append to domain `## Problems & Solutions` (symptom + root cause + fix + prevention + skill ref) | `domain-updater` |
|
|
4
22
|
|
|
5
|
-
|
|
6
|
-
2. **Fix** → Apply minimal fix
|
|
7
|
-
3. **Test** → Add regression test (tester agent)
|
|
8
|
-
4. **Document** → Record in domain Problems & Solutions
|
|
9
|
-
5. **Commit** → commit-manager agent
|
|
23
|
+
`domain-updater` deduplicates by symptom/root-cause. If the same bug recurred, it appends a "Recurrence" note instead of a duplicate entry.
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: research
|
|
3
|
+
description: Research best practices using research-web (MCP-first) with cache lookup before any web call.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# /research — Research Best Practices
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
Always read `.claude/config/active-project.json` first to know the active stack.
|
|
10
|
+
|
|
11
|
+
| # | Step | Tool / Skill |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| 1 | **Cache lookup** — check `.claude/skills/research-cache/cache/<topic>.md` first. If fresh (≤ 30 days), use it and skip steps 2–5 | `research-cache` |
|
|
14
|
+
| 2 | **MCP probe** — detect `mcp__web-scraper__*` availability (one-shot per session) | `research-web` |
|
|
15
|
+
| 3 | **Search** — Tier 1: `unified_search` (Brave + Vertex AI + Grok, dedupe + scoring). Tier 2 fallback: built-in `WebSearch` | `research-web` |
|
|
16
|
+
| 4 | **Source ranking** — official docs > engineering blogs > GitHub issues > Stack Overflow. Reject anything > 18 months old without re-validation | (you) |
|
|
17
|
+
| 5 | **Deep read** — Tier 1: `scrape_url` (stealth + proxy). Tier 2 fallback: built-in `WebFetch` | `research-web` |
|
|
18
|
+
| 6 | **Document** — write `.claude/skills/research-cache/cache/<topic>.md` with: date · expiry (now + 30 days) · sources · TL;DR · actionable rules | `research-cache` |
|
|
19
|
+
| 7 | **Promote** — if any rule applies project-wide, propose adding it to `CLAUDE.md` (do NOT auto-edit; surface the proposal) | (you) |
|
|
4
20
|
|
|
5
|
-
|
|
6
|
-
2. **Search web** → "[topic] best practices [year] [stack]"
|
|
7
|
-
3. **Sources**: Official docs > Engineering blogs > OWASP > GitHub issues
|
|
8
|
-
4. **Document** → Create `.claude/skills/research-cache/cache/[topic].md`
|
|
9
|
-
5. **Extract rules** → Add actionable rules to CLAUDE.md if applicable
|
|
10
|
-
6. **Update cache** → Date + expiry (30 days)
|
|
21
|
+
Each cache entry has frontmatter `expires_on:` so a future session can detect stale notes without re-reading the body.
|
|
@@ -1,10 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: validate
|
|
3
|
+
description: Run the full quality gate (typecheck → lint → test → build) using the stack's commands from active-project.json.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# /validate — Run Full Validation
|
|
2
8
|
|
|
3
|
-
Read quality gates from `.claude/config/active-project.json` and run
|
|
9
|
+
Read the quality gates from `.claude/config/active-project.json#qualityGates` and run them in order. Stop at the first failure.
|
|
4
10
|
|
|
5
11
|
```bash
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
jq -r '.qualityGates' .claude/config/active-project.json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Execution order (each step blocks the next):
|
|
16
|
+
|
|
17
|
+
| # | Gate | Typical commands |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| 1 | **Typecheck** | `npx tsc --noEmit` · `mypy .` · `vendor/bin/phpstan analyse` |
|
|
20
|
+
| 2 | **Lint** | `npx eslint .` · `ruff check .` · `vendor/bin/pint --test` |
|
|
21
|
+
| 3 | **Unit tests** | `npx vitest run` · `pytest` · `vendor/bin/phpunit` |
|
|
22
|
+
| 4 | **E2E** (only if configured) | `npx playwright test` |
|
|
23
|
+
| 5 | **Build** | `npm run build` · `composer dump-autoload --optimize` |
|
|
24
|
+
|
|
25
|
+
Output format (one line per gate):
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
typecheck: PASS (1.4s)
|
|
29
|
+
lint: PASS (0.8s)
|
|
30
|
+
tests: PASS (45/45 in 6.2s)
|
|
31
|
+
e2e: SKIP (not configured)
|
|
32
|
+
build: PASS (3.1s)
|
|
33
|
+
|
|
34
|
+
✅ All gates passed — safe to invoke commit-manager
|
|
8
35
|
```
|
|
9
36
|
|
|
10
|
-
|
|
37
|
+
If any gate fails, print the failure output and exit non-zero. `commit-manager` will refuse to commit while validation is failing.
|
|
@@ -1,71 +1,68 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: codebase-knowledge
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "Cached domain knowledge consumed BEFORE implementing any feature. Reads `.claude/skills/codebase-knowledge/_index.json` (machine-readable, regenerated by `documenter`) for fast filter, then reads only the affected `domains/<slug>.md` files. Avoids re-exploring the codebase every session."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Codebase Knowledge — Domain
|
|
7
|
+
# Codebase Knowledge — Domain Knowledge Reader (v2.0.0)
|
|
7
8
|
|
|
8
9
|
**ALWAYS invoke BEFORE implementing any feature.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
This skill is the **read side** of the project memory layer maintained by the `documenter` and `domain-updater` agents. Its job is to load just enough context for the next change without re-discovering the whole codebase.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Domain Files Location
|
|
13
|
+
## Storage layout (maintained by `documenter` v2.0.0)
|
|
15
14
|
|
|
16
15
|
```
|
|
17
|
-
.claude/skills/codebase-knowledge/
|
|
18
|
-
├──
|
|
19
|
-
├──
|
|
20
|
-
├──
|
|
21
|
-
├──
|
|
22
|
-
└──
|
|
16
|
+
.claude/skills/codebase-knowledge/
|
|
17
|
+
├── SKILL.md # this file
|
|
18
|
+
├── TEMPLATE.md # template for new domain files (no version — it is a template)
|
|
19
|
+
├── _INDEX.md # human-readable list of all domains
|
|
20
|
+
├── _index.json # machine-readable index — SOURCE OF TRUTH for filter
|
|
21
|
+
└── domains/
|
|
22
|
+
├── <slug>.md # one file per domain. ≤ 8 KB / ~2k tokens / 200 lines
|
|
23
|
+
└── <slug>.archive.md # commits/wisdom older than the cap (read on demand only)
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
```markdown
|
|
28
|
-
# {Domain Name}
|
|
26
|
+
## Read protocol (token-efficient)
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
| # | Step | Tool |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| 1 | **Resolve which domain(s) you need** by glob/grep on the affected file paths against `.claude/config/domain-mapping.json` | Bash + jq |
|
|
31
|
+
| 2 | **Read `_index.json` first** — one query gives you `last_commit`, `tags`, `connections` for every domain | `jq '.domains[] \| select(.slug == "auth")' _index.json` |
|
|
32
|
+
| 3 | **Read only the matched `domains/<slug>.md`** files — never `cat domains/*.md` (that defeats the budget) | Read |
|
|
33
|
+
| 4 | **Follow `connections`** if the change crosses a domain boundary; read those neighbours too | Read |
|
|
34
|
+
| 5 | **Skip `<slug>.archive.md`** unless `_index.json` flags `status: archived` and you actually need history | Read |
|
|
34
35
|
|
|
35
|
-
##
|
|
36
|
-
| File | Purpose |
|
|
37
|
-
|------|---------|
|
|
38
|
-
| `path/file.ext` | Description |
|
|
36
|
+
## Domain file shape
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
| Domain | How They Connect |
|
|
42
|
-
|--------|-----------------|
|
|
43
|
-
| auth | Validates tokens before API calls |
|
|
38
|
+
Defined by `documenter` v2.0.0 — see `TEMPLATE.md` in this folder for the canonical layout. Required sections:
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
## Problems & Solutions
|
|
54
|
-
### Problem Title
|
|
55
|
-
**Problem:** What went wrong
|
|
56
|
-
**Solution:** How it was fixed
|
|
57
|
-
**Prevention:** How to avoid in future
|
|
58
|
-
```
|
|
40
|
+
- YAML frontmatter (`domain · tags · owner · last_commit · last_date · files_count · connections · status`)
|
|
41
|
+
- TL;DR (≤ 3 lines, front-loads boundary)
|
|
42
|
+
- Files table (path → role)
|
|
43
|
+
- Connections table (bidirectional: → and ← rows)
|
|
44
|
+
- Recent Commits (capped at 20)
|
|
45
|
+
- Attention Points (capped at 10)
|
|
46
|
+
- Problems & Solutions (capped at 5, append-only)
|
|
59
47
|
|
|
60
48
|
## Workflow
|
|
61
49
|
|
|
62
|
-
1. **Before coding**
|
|
63
|
-
2. **During coding**
|
|
64
|
-
3. **After
|
|
50
|
+
1. **Before coding** — read this skill, then `_index.json`, then the affected `domains/<slug>.md` files.
|
|
51
|
+
2. **During coding** — note any new connection, gotcha, or non-obvious decision (you do not write here directly).
|
|
52
|
+
3. **After commit** — `documenter` maps files+commits, `domain-updater` records the wisdom you collected.
|
|
65
53
|
|
|
66
54
|
## Rules
|
|
67
55
|
|
|
68
|
-
1. **
|
|
69
|
-
2. **
|
|
70
|
-
3. **
|
|
71
|
-
4. **
|
|
56
|
+
1. **READ `_index.json` FIRST** — it is regenerated each commit; it is the cheapest way to filter.
|
|
57
|
+
2. **NEVER `cat domains/*.md`** — you will blow the context budget.
|
|
58
|
+
3. **BIDIRECTIONAL CONNECTIONS** — if `auth.md` lists `→ api`, then `api.md` MUST list `← auth`. If you spot a missing reverse edge, surface it for `domain-updater`.
|
|
59
|
+
4. **ARCHIVE FILES ARE OPT-IN** — read `<slug>.archive.md` only when `_index.json` shows `status: archived` or when you need pre-cap history.
|
|
60
|
+
5. **DO NOT EDIT DOMAIN FILES DIRECTLY** — only `documenter` and `domain-updater` write here. If something is wrong, fix the agent or report drift.
|
|
61
|
+
6. **TRUST `last_commit`** — if `_index.json#last_commit` ≠ HEAD, the documenter forgot to run; pause and fix instead of working with stale knowledge.
|
|
62
|
+
|
|
63
|
+
## See Also
|
|
64
|
+
|
|
65
|
+
- `documenter` v2.0.0 — writes to this layout (after every commit)
|
|
66
|
+
- `domain-updater` v2.0.0 — writes session wisdom (Problems & Solutions, Attention Points)
|
|
67
|
+
- `docs-tracker` v2.0.0 — file → domain mapping rules
|
|
68
|
+
- `claude-md-compactor` v2.0.0 — keeps `CLAUDE.md` ≤ 20 KB; this layer keeps each domain ≤ 8 KB
|
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
domain: <slug>
|
|
3
|
+
tags: [tag1, tag2]
|
|
4
|
+
owner: <team or "shared">
|
|
5
|
+
last_commit: <short-sha>
|
|
6
|
+
last_date: YYYY-MM-DD
|
|
7
|
+
files_count: 0
|
|
8
|
+
connections: []
|
|
9
|
+
status: active
|
|
10
|
+
---
|
|
2
11
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
# <Title> Domain
|
|
13
|
+
|
|
14
|
+
> **TL;DR** (≤ 3 lines). What lives here, where the boundary is, who calls it.
|
|
15
|
+
> Example: "User session lifecycle. Owns Sanctum cookie, login, password reset.
|
|
16
|
+
> Does NOT own user profile data (see `users` domain)."
|
|
7
17
|
|
|
8
18
|
## Files
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
|
|
20
|
+
| Path | Role |
|
|
21
|
+
|---|---|
|
|
11
22
|
|
|
12
23
|
## Connections
|
|
13
|
-
| Domain | How They Connect |
|
|
14
|
-
|--------|-----------------|
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
| Direction | Domain | What flows |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
|
|
28
|
+
## Recent Commits (capped at 20 — oldest auto-archived)
|
|
29
|
+
|
|
30
|
+
| Hash | Date | Subject |
|
|
31
|
+
|---|---|---|
|
|
19
32
|
|
|
20
33
|
## Attention Points
|
|
21
|
-
-
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
(≤ 10 entries; oldest beyond 10 → `<slug>.archive.md`)
|
|
36
|
+
|
|
37
|
+
## Problems & Solutions (append-only)
|
|
38
|
+
|
|
39
|
+
(≤ 5 entries; oldest beyond 5 → `<slug>.archive.md`)
|
|
40
|
+
|
|
41
|
+
## See Also
|
|
42
|
+
|
|
43
|
+
- Skill: `<skill-name §section>`
|
|
44
|
+
- Domain: `<related-slug>`
|
|
@@ -1,52 +1,205 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docker-patterns
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "Docker patterns for 2026: BuildKit + Buildx Bake (default builder for Docker Compose since June 2025), multi-stage builds, distroless / chiseled bases (90% size reduction, no shell), non-root users, .dockerignore, healthchecks, additional_contexts for service deps, secrets via build mounts (not COPY), reproducible builds. Stack examples: Node 22, Python 3.13 (uv), PHP 8.4 (FPM + Nginx)."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Docker Patterns
|
|
7
|
+
# Docker Patterns (2026)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
**Invoke when writing or modifying any `Dockerfile`, `docker-compose.yml`, `docker-bake.hcl`, or container build config.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
FROM php:8.3-fpm-alpine
|
|
11
|
+
> 2026 reality: BuildKit is the only builder; Bake is the default for Compose multi-image projects (since June 2025); distroless / chiseled bases are the default for production runtime; non-root + readonly rootfs is the default. **Anything else needs justification.**
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
libzip-dev \
|
|
15
|
-
&& docker-php-ext-install pdo_mysql zip opcache
|
|
13
|
+
## 1. The four non-negotiables
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
1. **Multi-stage build** — separate build deps from runtime deps. No exceptions.
|
|
16
|
+
2. **Distroless or chiseled runtime** — no shell, no package manager, no curl. ~2 MB instead of ~120 MB.
|
|
17
|
+
3. **Non-root user** — `USER 10001:10001` in the runtime stage. Never `root`.
|
|
18
|
+
4. **`.dockerignore`** — at minimum: `node_modules`, `vendor`, `.git`, `.env*`, `*.log`, `dist`, `build`, `coverage`, `__pycache__`, `.venv`.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
RUN chown -R www-data:www-data /var/www/html
|
|
20
|
+
## 2. Node.js 22 — production-grade Dockerfile
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
```dockerfile
|
|
23
|
+
# syntax=docker/dockerfile:1.7
|
|
24
|
+
ARG NODE_VERSION=22.11.0
|
|
25
|
+
|
|
26
|
+
# ---------- builder ----------
|
|
27
|
+
FROM node:${NODE_VERSION}-alpine AS builder
|
|
28
|
+
WORKDIR /app
|
|
29
|
+
COPY package*.json ./
|
|
30
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
31
|
+
npm ci --ignore-scripts
|
|
32
|
+
COPY . .
|
|
33
|
+
RUN npm run build && npm prune --omit=dev
|
|
34
|
+
|
|
35
|
+
# ---------- runtime: distroless, no shell ----------
|
|
36
|
+
FROM gcr.io/distroless/nodejs22-debian12:nonroot AS runtime
|
|
37
|
+
WORKDIR /app
|
|
38
|
+
COPY --from=builder --chown=nonroot:nonroot /app/node_modules ./node_modules
|
|
39
|
+
COPY --from=builder --chown=nonroot:nonroot /app/dist ./dist
|
|
40
|
+
COPY --from=builder --chown=nonroot:nonroot /app/package.json ./
|
|
41
|
+
USER nonroot
|
|
42
|
+
EXPOSE 3000
|
|
43
|
+
ENV NODE_ENV=production
|
|
44
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
45
|
+
CMD ["/nodejs/bin/node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
46
|
+
CMD ["dist/server.js"]
|
|
26
47
|
```
|
|
27
48
|
|
|
28
|
-
|
|
49
|
+
Key 2026 patterns: `--mount=type=cache` for npm, `--ignore-scripts` (supply-chain hardening — see `secrets-management` §2025-A03), distroless `nonroot` tag, `--chown` on COPY, healthcheck against `/healthz`.
|
|
50
|
+
|
|
51
|
+
## 3. Python 3.13 + `uv` — production-grade Dockerfile
|
|
29
52
|
|
|
30
53
|
```dockerfile
|
|
31
|
-
|
|
54
|
+
# syntax=docker/dockerfile:1.7
|
|
55
|
+
FROM python:3.13-slim-bookworm AS builder
|
|
56
|
+
COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /usr/local/bin/uv
|
|
32
57
|
WORKDIR /app
|
|
33
|
-
COPY
|
|
34
|
-
RUN
|
|
58
|
+
COPY pyproject.toml uv.lock ./
|
|
59
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
60
|
+
uv sync --frozen --no-dev --no-install-project
|
|
35
61
|
COPY . .
|
|
36
|
-
RUN
|
|
62
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
63
|
+
uv sync --frozen --no-dev
|
|
37
64
|
|
|
38
|
-
FROM
|
|
65
|
+
FROM gcr.io/distroless/python3-debian12:nonroot AS runtime
|
|
39
66
|
WORKDIR /app
|
|
40
|
-
COPY --from=builder /app/
|
|
41
|
-
|
|
42
|
-
EXPOSE
|
|
43
|
-
|
|
67
|
+
COPY --from=builder --chown=nonroot:nonroot /app /app
|
|
68
|
+
USER nonroot
|
|
69
|
+
EXPOSE 8000
|
|
70
|
+
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
|
|
71
|
+
CMD ["/app/.venv/bin/uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 4. PHP 8.4 (FPM + Nginx) with Composer install in builder
|
|
75
|
+
|
|
76
|
+
```dockerfile
|
|
77
|
+
# syntax=docker/dockerfile:1.7
|
|
78
|
+
FROM composer:2 AS vendor
|
|
79
|
+
WORKDIR /app
|
|
80
|
+
COPY composer.json composer.lock ./
|
|
81
|
+
RUN composer install --no-dev --no-interaction --no-scripts --prefer-dist --optimize-autoloader
|
|
82
|
+
|
|
83
|
+
FROM php:8.4-fpm-alpine AS runtime
|
|
84
|
+
RUN apk add --no-cache libzip-dev oniguruma-dev icu-dev \
|
|
85
|
+
&& docker-php-ext-install pdo_mysql zip opcache intl bcmath \
|
|
86
|
+
&& rm -rf /var/cache/apk/*
|
|
87
|
+
WORKDIR /var/www/html
|
|
88
|
+
COPY --from=vendor /app/vendor ./vendor
|
|
89
|
+
COPY --chown=www-data:www-data . .
|
|
90
|
+
USER www-data
|
|
91
|
+
EXPOSE 9000
|
|
92
|
+
HEALTHCHECK --interval=30s --timeout=3s CMD php-fpm-healthcheck || exit 1
|
|
93
|
+
CMD ["php-fpm"]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 5. Compose with `additional_contexts` — service deps without sequential builds
|
|
97
|
+
|
|
98
|
+
For a project where service B depends on service A's image, **don't** rely on `depends_on` for build order — declare a build-time dependency:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
# docker-compose.yml
|
|
102
|
+
services:
|
|
103
|
+
api:
|
|
104
|
+
build:
|
|
105
|
+
context: ./api
|
|
106
|
+
dockerfile: Dockerfile
|
|
107
|
+
|
|
108
|
+
worker:
|
|
109
|
+
build:
|
|
110
|
+
context: ./worker
|
|
111
|
+
dockerfile: Dockerfile
|
|
112
|
+
additional_contexts:
|
|
113
|
+
api: "service:api" # build worker AFTER api, with api's image as a context
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 6. Bake — the default Compose builder (since Jun 2025)
|
|
117
|
+
|
|
118
|
+
Compose v2 now invokes Buildx Bake by default for multi-service projects. Opt out with `COMPOSE_BAKE=false` only if you need a feature Bake doesn't support (rare).
|
|
119
|
+
|
|
120
|
+
```hcl
|
|
121
|
+
# docker-bake.hcl — explicit form for CI / multi-arch
|
|
122
|
+
variable "TAG" { default = "latest" }
|
|
123
|
+
|
|
124
|
+
group "default" {
|
|
125
|
+
targets = ["api", "worker"]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
target "api" {
|
|
129
|
+
context = "./api"
|
|
130
|
+
dockerfile = "Dockerfile"
|
|
131
|
+
platforms = ["linux/amd64", "linux/arm64"]
|
|
132
|
+
tags = ["myorg/api:${TAG}"]
|
|
133
|
+
cache-from = ["type=gha"]
|
|
134
|
+
cache-to = ["type=gha,mode=max"]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
target "worker" {
|
|
138
|
+
inherits = ["api"]
|
|
139
|
+
context = "./worker"
|
|
140
|
+
tags = ["myorg/worker:${TAG}"]
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Build everything in parallel, push to registry, with cache shared via GitHub Actions
|
|
146
|
+
TAG=v1.2.3 docker buildx bake --push
|
|
44
147
|
```
|
|
45
148
|
|
|
46
|
-
##
|
|
149
|
+
## 7. Build-time secrets — `--mount=type=secret`, never `COPY .env`
|
|
150
|
+
|
|
151
|
+
```dockerfile
|
|
152
|
+
# syntax=docker/dockerfile:1.7
|
|
153
|
+
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
|
|
154
|
+
npm ci
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
docker buildx build --secret id=npmrc,src=$HOME/.npmrc .
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The secret never lands in any image layer. **Never** `COPY .env` or `ENV API_KEY=...`. See `secrets-management`.
|
|
162
|
+
|
|
163
|
+
## 8. Healthchecks — match the orchestrator's expectation
|
|
164
|
+
|
|
165
|
+
```dockerfile
|
|
166
|
+
# Application-level check; fast; idempotent.
|
|
167
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
168
|
+
CMD wget -qO- http://127.0.0.1:3000/healthz || exit 1
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For Kubernetes the Dockerfile HEALTHCHECK is informational only — the cluster uses `livenessProbe` / `readinessProbe`. See `observability` §7.
|
|
172
|
+
|
|
173
|
+
## 9. Image hardening checklist
|
|
174
|
+
|
|
175
|
+
- [ ] Multi-stage build present
|
|
176
|
+
- [ ] Runtime stage is distroless / chiseled / scratch (no shell, no package manager)
|
|
177
|
+
- [ ] `USER` directive set to non-root before `CMD`
|
|
178
|
+
- [ ] `.dockerignore` excludes `.env*`, `node_modules`, `.git`, build outputs
|
|
179
|
+
- [ ] No secrets baked into layers (`docker history --no-trunc IMAGE` clean)
|
|
180
|
+
- [ ] `HEALTHCHECK` directive present
|
|
181
|
+
- [ ] Image pinned by digest in production manifests (`image@sha256:...`)
|
|
182
|
+
- [ ] `docker scout cves IMAGE` (or `trivy image IMAGE`) clean of HIGH/CRITICAL
|
|
183
|
+
- [ ] Build is reproducible — same input → same digest
|
|
184
|
+
|
|
185
|
+
## FORBIDDEN
|
|
186
|
+
|
|
187
|
+
| Pattern | Why |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `FROM ubuntu:latest` (or any `latest`) | Unpinned, unreproducible |
|
|
190
|
+
| `RUN apt-get install ...` without `--no-install-recommends` and `rm -rf /var/lib/apt/lists/*` | Bloated layers, stale package cache |
|
|
191
|
+
| `COPY .env .` | Secrets in image layer forever |
|
|
192
|
+
| `ENV API_KEY=...` in Dockerfile | Same |
|
|
193
|
+
| `USER root` in runtime stage | Container escape blast radius |
|
|
194
|
+
| `chmod 777` on app dirs | Privilege escalation in shared envs |
|
|
195
|
+
| `--privileged` containers without explicit security review | Almost always wrong |
|
|
196
|
+
| `curl ... | sh` in RUN | Unverifiable supply chain |
|
|
197
|
+
| Single-stage build with dev deps in final image | Bloated, exposes build tools |
|
|
198
|
+
| `HEALTHCHECK NONE` | Orchestrator can't tell if app is wedged |
|
|
199
|
+
|
|
200
|
+
## See Also
|
|
47
201
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
5. **Health checks** — always include
|
|
202
|
+
- `secrets-management` — build-time secret mounts, OIDC for registry push
|
|
203
|
+
- `security-baseline` (2025-A03) — supply chain: pin actions by SHA, sign images (Sigstore/cosign)
|
|
204
|
+
- `observability` — `/healthz` + `/readyz` semantics
|
|
205
|
+
- `ci-pipelines` — building & pushing in CI
|