Containerize docs site tooling for security and update documentation (#4942)

* Containerize the docs site's npm tooling

Run Astro/Starlight dev, build, and preview commands through Docker
(docs/Dockerfile, docker-compose.yml, service winutil-astro) instead
of bare npm on the host, and document the required commands and
rationale in docs/README.md.

* Document Docker-only npm policy for agents

Add a Dependency Installs, Builds, And Dev Servers section to
AGENTS.md requiring docs/ tooling to run through Docker rather than
directly on the host, point SPEC.md's Docs Site section at the new
Dockerfile/docker-compose.yml, and renumber the remaining AGENTS.md
sections to stay sequential.

* Harden docs Docker dev environment

Tightened docs-container safety and clarified contributor workflow. The docs Docker image now switches to the non-root `node` user after setting ownership, and compose now binds Astro to `127.0.0.1` instead of all interfaces. Updated AGENTS and docs README instructions to explain the security boundary of the bind mount and to require rebuilding plus `docker compose down -v` after dependency changes so `node_modules` is reseeded correctly.

* Clarify docs secret handling in AGENTS

Updates AGENTS.md to tighten docs security guidance: secrets must not be stored anywhere under `docs/`, because `docs/.dockerignore` only affects image build context and does not protect files from the Docker Compose bind mount used for docs dev/build commands.

* Fix preview command to expose port in Docker

The previous preview command didn't expose the port outside the container. Adding --service-ports and binding to 0.0.0.0 makes the preview server accessible from the host.
This commit is contained in:
Sean (ANGRYxScotsman)
2026-08-09 20:11:35 -05:00
committed by GitHub
parent ea7fcf9d2b
commit 1458327638
6 changed files with 96 additions and 33 deletions
+26 -15
View File
@@ -42,25 +42,36 @@ These rules override everything else in this file when in conflict:
```powershell
Invoke-ScriptAnalyzer -Path . -Settings .\lint\PSScriptAnalyser.ps1 -Recurse
```
- Docs site dev server (run from `docs/`):
- Docs site dev server (run from `docs/`; see Section 2 for why this goes through Docker):
```powershell
npm install
npm run dev
docker compose up winutil-astro
```
- Docs site production build (run from `docs/`):
```powershell
npm run build
docker compose run --rm winutil-astro npm run build
```
Prefer the narrowest useful verification while iterating. Use the full relevant check before finishing.
## 2. Source Of Truth
## 2. Dependency Installs, Builds, And Dev Servers
Given the current wave of npm/pnpm/yarn supply-chain worms (malicious postinstall/preinstall scripts, credential-stealing packages): **never run npm/pnpm/yarn/npx directly on the host, full stop.** The docs site (`docs/`) is the only npm-based project in this repo; always run its tooling inside Docker via `docs/Dockerfile` and `docs/docker-compose.yml` (service `winutil-astro`).
- Never run `npm install`, `npm run <script>`, `npx <pkg>`, `pnpm`, or `yarn` directly on the host shell in `docs/`. Use `docker compose run --rm winutil-astro <command>` / `docker compose up winutil-astro` instead (see Section 1 for the exact commands).
- If a task needs a new docs dependency, add it to `docs/package.json` yourself, then rebuild the image and drop the `node_modules` volume so it repopulates from the new image (run from `docs/`): `docker compose build winutil-astro`, then `docker compose down -v`. Docker only seeds a named volume from the image the first time it's created, so a plain rebuild silently leaves the old `node_modules` in place. Don't install packages on the host, even temporarily, "just to check something."
- If Docker isn't available on the host, propose the install command for the current OS and wait for confirmation before running it — don't fall back to running npm on the host instead. If the daemon just isn't running (Docker is installed but not started), tell the user rather than trying to start it yourself.
- Treat any `postinstall`/`preinstall` lifecycle script in a new dependency as worth flagging to the user before installing — summarize what it does.
- Don't put real secrets anywhere under `docs/`. `docs/.dockerignore` only trims what `docker build` copies into the image — it does not affect the `docker compose` bind mount, which exposes the entire `docs/` directory (including any `.env` file) inside the container for every dev/build/preview command (see the next bullet). There is no "keep it out unless mounted" middle ground here.
- The container mounts `docs/` as a volume, so file edits on the host are reflected inside the container immediately — no rebuild needed for normal code changes, only when `docs/package.json`/`docs/package-lock.json` change (see the rebuild-and-drop-volume steps above).
- This Docker requirement is specific to `docs/`. The rest of the repo is PowerShell (`Compile.ps1`, Pester, Script Analyzer) and runs directly on the host per Section 1.
## 3. Source Of Truth
For changes that affect the compiled WinUtil script, make them only in the source files described in SPEC.md's Repository Layout — never in `winutil.ps1` itself. If behavior changes require the compiled script to change, update the source files and run `.\Compile.ps1` only to verify generation.
This scoping applies to compiled-script behavior only. Repository metadata — `AGENTS.md`, `SPEC.md`, `CLAUDE.md`/`GEMINI.md`/`.github/copilot-instructions.md`, `.github/workflows/`, and the root `.gitignore` — is edited directly when a task requires it, per the other sections of this file.
## 3. Before Editing
## 4. Before Editing
- State the plan in one or two sentences before editing. For non-trivial work, include the verification you intend to run.
- Read the files you will touch and the files that call them.
@@ -68,7 +79,7 @@ This scoping applies to compiled-script behavior only. Repository metadata — `
- Surface assumptions when they affect behavior, compatibility, or user data.
- If two approaches have meaningful tradeoffs, name them before choosing. Trivial tasks can proceed directly.
## 4. Coding Guidelines
## 5. Coding Guidelines
- Prefer the minimum code that solves the stated problem.
- Keep PowerShell functions in one function file when practical, with the file name matching the primary function name.
@@ -81,7 +92,7 @@ This scoping applies to compiled-script behavior only. Repository metadata — `
- Clean up orphans created by your own changes, such as unused variables or functions made obsolete by the edit.
- Avoid broad formatting-only edits, especially in JSON config files, XAML, docs, and generated output.
## 5. Runtime And Safety Rules
## 6. Runtime And Safety Rules
- WinUtil performs system-level Windows changes; treat registry, services, AppX removal, package manager, Windows Update, ISO, and unattended setup changes as high-risk (see SPEC.md's Safety Requirements).
- Prefer existing helper functions for WinGet, Chocolatey, registry, services, progress, and UI updates.
@@ -90,7 +101,7 @@ This scoping applies to compiled-script behavior only. Repository metadata — `
- Avoid storing credentials, secrets, or machine-specific paths in repo files.
- Preserve logging and user feedback patterns for long-running or destructive operations.
## 6. Surgical Changes
## 7. Surgical Changes
- Do not improve adjacent code, comments, formatting, imports, or docs unless required.
- Do not refactor working code because you are already in the file.
@@ -98,7 +109,7 @@ This scoping applies to compiled-script behavior only. Repository metadata — `
- Keep diffs reviewable. Every changed line should trace to the user's request.
- If a change starts spreading across unrelated areas, pause and reassess the plan.
## 7. Verification
## 8. Verification
Define success in terms that can be checked, then check it.
@@ -112,7 +123,7 @@ Define success in terms that can be checked, then check it.
If a check cannot be run, say exactly why and what residual risk remains. See SPEC.md's Testing And CI for what GitHub Actions runs on every push.
## 8. Generated Files And Git Hygiene
## 9. Generated Files And Git Hygiene
- Treat local `winutil.ps1` changes as disposable compile output.
- Never stage or commit `winutil.ps1`, `binary/`, or anything else ignored by the root `.gitignore` or `docs/.gitignore` — read those files rather than assuming. `docs/public/` is tracked source for static assets, not generated output.
@@ -122,7 +133,7 @@ If a check cannot be run, say exactly why and what residual risk remains. See SP
- Commit messages, when requested, should be descriptive: short subject under 72 characters, body explaining why when needed.
- When committing, split changes into small, logical commits rather than one large commit, so each commit's diff is reviewable as a single group of related changes.
## 9. Documentation Expectations
## 10. Documentation Expectations
- Update `docs/src/content/docs/guides/` when user-facing behavior changes.
- Update `docs/src/content/docs/code-reference/architecture.mdx` and other hand-written developer docs when architecture, build flow, config schema, or contribution workflow changes — but never hand-edit the auto-generated `code-reference/tweaks/` or `code-reference/features/` subfolders (see Non-Negotiables).
@@ -131,7 +142,7 @@ If a check cannot be run, say exactly why and what residual risk remains. See SP
- Put detailed user and developer documentation under `docs/`.
- Keep SPEC.md aligned with project/architecture changes, and this file aligned with process changes.
## 10. Communication Style
## 11. Communication Style
- Be direct and concise. Start with the answer or action.
- No flattery, filler, ceremonial closings, or fake certainty.
@@ -139,7 +150,7 @@ If a check cannot be run, say exactly why and what residual risk remains. See SP
- Report what changed, how it was verified, and anything not done.
- If the user asks for a review, lead with findings and file/line references.
## 11. When To Ask
## 12. When To Ask
Ask before proceeding when:
@@ -154,7 +165,7 @@ Proceed without asking when:
- Ambiguity can be resolved by reading the code or running a local command.
- The user already answered the question in this session.
## 12. Project Learnings
## 13. Project Learnings
When the user corrects an agent approach, add or tighten one concrete rule here before ending the session. Keep this section short and prune rules that no longer matter.
+1
View File
@@ -97,6 +97,7 @@ Because the final script is concatenated, code cannot rely on runtime module imp
- `docs/src/content/docs/code-reference/tweaks/` and `.../features/` are auto-generated by `tools/devdocs-generator.ps1` from `config/tweaks.json`/`config/feature.json` and the relevant PowerShell function files. Other pages under `code-reference/` (e.g. `architecture.mdx`) are hand-written and untouched by the generator.
- Sidebar entries in `docs/astro.config.mjs` must match actual page slugs under `docs/src/content/docs/`.
- `docs/public/` is tracked source for static assets (favicons, etc.), not generated output. Generated/ignored paths are listed in `docs/.gitignore` (`dist/`, `.astro/`, `node_modules/`, local env files).
- `docs/Dockerfile` and `docs/docker-compose.yml` (service `winutil-astro`) containerize the site's npm tooling; see AGENTS.md's Dependency Installs, Builds, And Dev Servers for why and how agents must use them instead of running npm on the host.
## Testing And CI
+7
View File
@@ -0,0 +1,7 @@
node_modules
.astro
dist
.git
.env
.env.*
*.log
+16
View File
@@ -0,0 +1,16 @@
FROM node:22-bookworm-slim
RUN corepack enable
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN chown -R node:node /app
USER node
EXPOSE 4321
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
+30 -18
View File
@@ -1,26 +1,24 @@
# Starlight Starter Kit: Basics
# WinUtil Docs
[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build)
```
npm create astro@latest -- --template starlight
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
Documentation site for [WinUtil](https://github.com/ChrisTitusTech/winutil), built with [Astro](https://astro.build) and [Starlight](https://starlight.astro.build). Served at [winutil.christitus.com](https://winutil.christitus.com/).
## 🚀 Project Structure
Inside of your Astro + Starlight project, you'll see the following folders and files:
```
.
├── public/
├── src/
│ ├── assets/
│ ├── components/
│ ├── content/
│ │ └── docs/
│ ├── styles/
│ └── content.config.ts
├── astro.config.mjs
├── docker-compose.yml
├── Dockerfile
├── package.json
└── tsconfig.json
```
@@ -33,17 +31,31 @@ Static assets, like favicons, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
All commands run in a Docker container — there's no need to install Node or npm dependencies on your host. This is deliberate, not just convenience: npm/pnpm/yarn have seen a steady stream of supply-chain attacks (malicious `postinstall`/`preinstall` scripts, credential-stealing packages), so `npm install` and friends never run directly on a contributor's machine here. Note the container still has read-write access to this `docs/` directory (it's bind-mounted for live reload), so this only contains a compromised package to the project folder plus the container itself — it doesn't reach the rest of your host (SSH keys, other repos, cloud credentials elsewhere on disk). Don't keep real secrets in `docs/` as a result.
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
[Docker](https://www.docker.com/) (with Compose) is required — install Docker Desktop (or Docker Engine + the `docker compose` plugin on Linux) and make sure the daemon is running before using any of the commands below.
All commands are run from the `docs/` directory, from a terminal:
| Command | Action |
| :------------------------------------------------ | :----------------------------------------------- |
| `docker compose build` | Builds the dev image (needed after Dockerfile or dependency changes) |
| `docker compose up winutil-astro` | Starts local dev server at `localhost:4321` |
| `docker compose run --rm winutil-astro npm run build` | Build the production site to `./dist/` |
| `docker compose run --rm --service-ports winutil-astro npm run preview -- --host 0.0.0.0` | Preview the build locally, before deploying |
| `docker compose run --rm winutil-astro npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `docker compose down` | Stop and remove the dev container |
Source files are bind-mounted into the container, so edits on the host are picked up immediately by the dev server — no rebuild needed for normal content or code changes. After changing `package.json`, `package-lock.json`, or the `Dockerfile`, rebuild the image *and* drop the `node_modules` volume, since Docker only seeds a named volume from the image the first time it's created — a plain rebuild leaves the old `node_modules` in place:
```sh
docker compose build
docker compose down -v
docker compose up winutil-astro
```
The first `docker compose up` (or any command before an image exists) builds the image and runs `npm install` from scratch, which can take a few minutes. Subsequent runs reuse the cached image and start almost immediately.
## 👀 Want to learn more?
Check out [Starlights docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
Check out [Starlight's docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
+16
View File
@@ -0,0 +1,16 @@
services:
winutil-astro:
build: .
ports:
- "127.0.0.1:4321:4321"
volumes:
- .:/app
- astro_node_modules:/app/node_modules
tmpfs:
- /app/.astro
environment:
- CHOKIDAR_USEPOLLING=true
- ASTRO_TELEMETRY_DISABLED=1
volumes:
astro_node_modules: