From 2b3b53f247fae6eca708d390c183c587c2f39c86 Mon Sep 17 00:00:00 2001 From: w7-mgfcode Date: Sat, 27 Jun 2026 15:20:55 +0200 Subject: [PATCH 1/3] docs: restore CLAUDE.md and re-track it Recreate the root CLAUDE.md (project context for Claude Code) and remove it from .gitignore so it is committed and shared again; .claude/ stays ignored. The file captures per-project commands, the four validation gates, and the non-obvious gotchas surfaced during end-to-end validation (set -e increment footgun, stderr/stdout JSON discipline, adminer dual-network/loopback, the spamd-is-not-a- milter trap, php-fpm TCP, init.sql/${MYSQL_DATABASE}, scratch-image healthchecks). --- .gitignore | 1 - CLAUDE.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index d9edb15..a97ff4e 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,3 @@ build/ # Agent/assistant local files (not tracked) .claude/ -CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..31208aa --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# CLAUDE.md + +Project context for Claude Code. A bilingual (EN/HU) Linux system-administration +portfolio of three independent, fully containerized projects. Each project runs +standalone via `docker compose`. + +## Repository layout + +| Path | What | +|------|------| +| `project-01-lamp-monitoring/` | LAMP stack (nginx, php-fpm 8.2, mysql 8.0, adminer) + Bash monitoring/backup scripts | +| `project-02-mail-server/` | Postfix/Dovecot/SpamAssassin/Roundcube mail stack + PHP dashboard + test suite | +| `project-03-infra-automation/` | 6 sysadmin Bash tools + Docker multi-OS test harness (debian/alpine/ubuntu/coredns) | +| `docs/` | Cross-project docs (ARCHITECTURE, DEPLOYMENT, SCRIPTS, CHANGELOG, PROJECT-0X-TESTING) — all bilingual | +| `plans/00-start_plan.md` | Original full implementation spec (47 KB) | +| `.github/` | CI (`workflows/ci.yml`), PR/issue templates, CODEOWNERS, dependabot, SECURITY | + +## Common commands + +Each project is self-contained; `cd` into it first. + +```bash +cp .env.example .env # required before first run (sets DB passwords etc.) +docker compose up -d --build # start the stack +docker compose ps # check health +docker compose logs -f +docker compose down -v # tear down + remove volumes (full reset) +``` + +Validation gates (what CI also runs — keep all four green): + +```bash +shellcheck -x # static analysis; CI uses v0.10.0, --severity=warning +docker compose -f /docker-compose.yml --project-directory config -q +cd project-02-mail-server && bash tests/run-all-tests.sh # 42 tests +cd project-03-infra-automation && bash tests/e2e-test.sh # 43 tests (TAP output) +``` + +## Per-project notes + +**project-01** — scripts: `log-analyzer.sh` (showcase), `backup.sh`, `health-check.sh`. No `tests/` dir; validate by health + valid JSON output. Endpoints: nginx `:80`, Adminer `127.0.0.1:8080`. + +**project-02** — services: `cert-init` (one-shot SSL init, exits 0), `mysql`, `postfix`, `dovecot`, `spamassassin`, `roundcube`, `dashboard`. Scripts under `scripts/` (+ `lib/common.sh`). Tests under `tests/`. Schema: `mysql/init.sql`. + +**project-03** — tools: `server-hardening.sh` (showcase), `network-diagnostics.sh`, `service-watchdog.sh`, `backup-manager.sh`, `log-rotation.sh`, `system-inventory.sh` (+ shared `lib/common.sh`). Test harness: `tests/e2e-test.sh` drives debian/alpine/ubuntu/nginx/coredns containers. `server-hardening.sh` CLI: `--check` (audit/dry-run), `--fix`, `--modules ssh,kernel`. + +## Gotchas (non-obvious, learned the hard way) + +- **`set -euo pipefail` + `((var++))` aborts the script** when `var` is 0 (post-increment returns the old value → non-zero exit). Always use `var=$((var+1))`. +- **Scripts that emit JSON** (e.g. `health-check.sh`): send `log()` to **stderr**, keep stdout clean — command substitutions that capture stdout otherwise corrupt the JSON. +- **Sourcing the shared lib**: add `# shellcheck source-path=SCRIPTDIR` and run `shellcheck -x` so sources resolve. Shared table/format helpers (`print_table_*`) live in `scripts/lib/common.sh`, not in individual scripts. +- **P01 Adminer** needs **both** networks: `frontend` (to publish its port) and `backend` (to reach MySQL; `backend` is `internal: true` and cannot expose ports). Its port is bound to `127.0.0.1:8080` (loopback) on purpose — it's a high-privilege DB UI. The `adminer:4.8.1` image ships no curl/wget, so its healthcheck uses bundled PHP. +- **P02 Postfix milter**: SpamAssassin `spamd` (port 783) speaks SPAMC/SPAMD, **not** the milter protocol — never point `smtpd_milters` at it (it aborts every SMTP session). SMTP-time scanning needs `spamass-milter` or a `content_filter`. Container logging needs `maillog_file = /dev/stdout` + a `postlog` service (no syslog in the image). +- **P02 php-fpm** official image forces TCP via `zz-docker.conf` (`listen = 9000`); nginx must use `fastcgi_pass 127.0.0.1:9000`, not a unix socket. +- **P02 MySQL `init.sql`**: do **not** hardcode `USE ` — the image already selects `${MYSQL_DATABASE}`. Note `mysql/*.sql` is gitignored, so `init.sql` is tracked only via the `!mysql/init.sql` exception in that project's `.gitignore`. +- **P03 target containers**: don't add a compose `command:` override — it replaces the image CMD (`sleep infinity`) and the container exits/restart-loops. Let the entrypoint's `exec "$@"` keep it alive. +- **P03 CoreDNS** is built `FROM scratch` (no shell/nc/curl) → an exec healthcheck is impossible; rely on the Corefile `health :8080` plugin + restart policy instead. + +## Conventions + +- **Bash**: `set -euo pipefail`; must pass `shellcheck -x --severity=warning` (advisory info/style notes like intentional SC2016 single-quoting are acceptable). +- **Docs**: bilingual English + Magyar; mirror both when editing prose. +- **`main` is protected**: no force-push, no deletion; required status checks **ShellCheck** + **Compose Validate** (`.github/workflows/ci.yml`). Land changes via PR. +- **Commits/PRs**: do **not** add Claude Code / AI attribution (no `Co-Authored-By` trailer, no "Generated with…" footer) — project owner's preference. From 19920d516e641cb68873c106fbba47edbac872ce Mon Sep 17 00:00:00 2001 From: w7-mgfcode Date: Sat, 27 Jun 2026 15:42:41 +0200 Subject: [PATCH 2/3] chore: gitignore .agents/ local assistant directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a97ff4e..70715de 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ build/ # Agent/assistant local files (not tracked) .claude/ +.agents/ From 9544dbf2037cb4dae6b140aa9f73c336c8a6320c Mon Sep 17 00:00:00 2001 From: w7-mgfcode Date: Sat, 27 Jun 2026 15:48:02 +0200 Subject: [PATCH 3/3] docs: make AGENTS.md the canonical agent doc; CLAUDE.md becomes a thin shim Track the existing root AGENTS.md as the single source of agent operating rules, and replace CLAUDE.md's duplicated policy with a thin shim that imports it (@AGENTS.md) plus Claude-specific runtime notes. Resolves the single-source-of- truth drift flagged by the maintaining-agent-docs validator (now 0 warnings). --- AGENTS.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 71 +++++++------------------------------------------------ 2 files changed, 72 insertions(+), 62 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..074e9af --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,63 @@ +# AGENTS.md + +## Project Overview + +This repository is a bilingual English/Hungarian Linux system-administration portfolio with three independent Docker Compose projects: + +- `project-01-lamp-monitoring/` - LAMP/LEMP stack with nginx, PHP-FPM, MySQL, Adminer, and Bash monitoring/backup scripts. +- `project-02-mail-server/` - Postfix, Dovecot, SpamAssassin, Roundcube, dashboard, and mail-server automation tests. +- `project-03-infra-automation/` - Bash infrastructure tools with a Docker-based multi-OS test harness. + +Shared documentation lives in `docs/`; project-specific documentation lives in each project directory. + +## Build and Test Commands + +Run commands from the repository root unless the command explicitly changes directory. + +- `find . -type f -name '*.sh' -not -path './.git/*' -print0 | xargs -0 shellcheck -x --severity=warning` - run after changing Bash scripts or tests. CI pins ShellCheck to v0.10.0 and treats warnings/errors as failures. +- `for dir in project-01-lamp-monitoring project-02-mail-server project-03-infra-automation; do [ -f "$dir/.env" ] || cp "$dir/.env.example" "$dir/.env"; docker compose -f "$dir/docker-compose.yml" --project-directory "$dir" config --quiet; done` - validate all Compose files like CI. +- `cd project-02-mail-server && bash tests/run-all-tests.sh` - run after changing the mail stack, dashboard, schema, or project-02 scripts. +- `cd project-03-infra-automation && bash tests/e2e-test.sh` - run after changing project-03 tools, containers, configs, or test harness. +- `cd && cp .env.example .env && docker compose up -d --build` - start a project stack for smoke testing. +- `cd && docker compose down -v` - tear down a project stack and remove volumes after destructive local test runs. + +## Code Style + +- Bash scripts should use `set -euo pipefail`, local lowercase variables, uppercase constants, and clear function names. +- When a script sources a shared library, add `# shellcheck source-path=SCRIPTDIR` and validate with `shellcheck -x`. +- Under `set -e`, do not use `((var++))` for counters because incrementing from zero returns a failing status; use `var=$((var+1))`. +- Keep stdout machine-readable when a script promises JSON output. Send logs and status messages to stderr. +- Docker images should use official, version-pinned bases where practical, health checks for long-running services, named volumes, and isolated backend networks for databases. +- Documentation is bilingual. When editing user-facing docs, update both English and Hungarian sections. + +## Architecture Notes + +- Each top-level `project-0*/` directory is self-contained and should be runnable with its own `.env` and `docker compose` commands. +- `project-01-lamp-monitoring/` has no dedicated test directory; validate with Compose config, stack health, endpoint smoke tests, and JSON cleanliness from `scripts/health-check.sh`. +- `project-02-mail-server/` uses `cert-init` as a one-shot SSL initializer; an exit code of 0 is expected and is not a failed service. +- SpamAssassin `spamd` speaks SPAMC/SPAMD on port 783, not the milter protocol. Do not wire Postfix `smtpd_milters` directly to `spamassassin:783`. +- Official PHP-FPM images listen on TCP `127.0.0.1:9000` through `zz-docker.conf`; nginx FastCGI configs should not assume a Unix socket unless the image is changed accordingly. +- MySQL init scripts should not hardcode `USE ` when the Docker image already selects `${MYSQL_DATABASE}`. +- In project 03, target container entrypoints end with `exec "$@"`; avoid Compose `command:` overrides that replace the intended `sleep infinity` keep-alive. +- CoreDNS is built from `scratch`; do not add exec health checks that require shell, `nc`, `curl`, or `wget`. + +## Validation Before Completion + +- For shell or Compose changes, run the matching commands from `Build and Test Commands` and report any command that could not be run. +- For project-02 and project-03 behavior changes, run their automated test suites unless the change is documentation-only or the environment lacks Docker. +- For documentation-only changes, verify Markdown content manually and preserve bilingual structure. +- Check `git status --short` before finishing so user changes are not mistaken for agent edits. + +## Security and Secrets + +- Do not commit `.env` files, private keys, credentials, tokens, generated certificates, or real mailbox data. +- Use `.env.example` for defaults and placeholders. +- Keep Adminer and other high-privilege admin surfaces loopback-only or internal unless the user explicitly asks for exposure. +- Do not remove network isolation that keeps databases and backend services off host-exposed networks. + +## Repository-Specific Rules + +- Preserve existing user changes; do not reset, overwrite, or clean unrelated work. +- Do not add AI attribution, `Generated with...` footers, or `Co-Authored-By` trailers. +- `main` is protected; expect changes to land through PRs with the `ShellCheck` and `Compose Validate` checks green. +- Closer nested `AGENTS.md` files, if added later, override these root instructions for their subtree. diff --git a/CLAUDE.md b/CLAUDE.md index 31208aa..2c65711 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,64 +1,11 @@ -# CLAUDE.md +@AGENTS.md -Project context for Claude Code. A bilingual (EN/HU) Linux system-administration -portfolio of three independent, fully containerized projects. Each project runs -standalone via `docker compose`. +## Claude-specific notes -## Repository layout - -| Path | What | -|------|------| -| `project-01-lamp-monitoring/` | LAMP stack (nginx, php-fpm 8.2, mysql 8.0, adminer) + Bash monitoring/backup scripts | -| `project-02-mail-server/` | Postfix/Dovecot/SpamAssassin/Roundcube mail stack + PHP dashboard + test suite | -| `project-03-infra-automation/` | 6 sysadmin Bash tools + Docker multi-OS test harness (debian/alpine/ubuntu/coredns) | -| `docs/` | Cross-project docs (ARCHITECTURE, DEPLOYMENT, SCRIPTS, CHANGELOG, PROJECT-0X-TESTING) — all bilingual | -| `plans/00-start_plan.md` | Original full implementation spec (47 KB) | -| `.github/` | CI (`workflows/ci.yml`), PR/issue templates, CODEOWNERS, dependabot, SECURITY | - -## Common commands - -Each project is self-contained; `cd` into it first. - -```bash -cp .env.example .env # required before first run (sets DB passwords etc.) -docker compose up -d --build # start the stack -docker compose ps # check health -docker compose logs -f -docker compose down -v # tear down + remove volumes (full reset) -``` - -Validation gates (what CI also runs — keep all four green): - -```bash -shellcheck -x # static analysis; CI uses v0.10.0, --severity=warning -docker compose -f /docker-compose.yml --project-directory config -q -cd project-02-mail-server && bash tests/run-all-tests.sh # 42 tests -cd project-03-infra-automation && bash tests/e2e-test.sh # 43 tests (TAP output) -``` - -## Per-project notes - -**project-01** — scripts: `log-analyzer.sh` (showcase), `backup.sh`, `health-check.sh`. No `tests/` dir; validate by health + valid JSON output. Endpoints: nginx `:80`, Adminer `127.0.0.1:8080`. - -**project-02** — services: `cert-init` (one-shot SSL init, exits 0), `mysql`, `postfix`, `dovecot`, `spamassassin`, `roundcube`, `dashboard`. Scripts under `scripts/` (+ `lib/common.sh`). Tests under `tests/`. Schema: `mysql/init.sql`. - -**project-03** — tools: `server-hardening.sh` (showcase), `network-diagnostics.sh`, `service-watchdog.sh`, `backup-manager.sh`, `log-rotation.sh`, `system-inventory.sh` (+ shared `lib/common.sh`). Test harness: `tests/e2e-test.sh` drives debian/alpine/ubuntu/nginx/coredns containers. `server-hardening.sh` CLI: `--check` (audit/dry-run), `--fix`, `--modules ssh,kernel`. - -## Gotchas (non-obvious, learned the hard way) - -- **`set -euo pipefail` + `((var++))` aborts the script** when `var` is 0 (post-increment returns the old value → non-zero exit). Always use `var=$((var+1))`. -- **Scripts that emit JSON** (e.g. `health-check.sh`): send `log()` to **stderr**, keep stdout clean — command substitutions that capture stdout otherwise corrupt the JSON. -- **Sourcing the shared lib**: add `# shellcheck source-path=SCRIPTDIR` and run `shellcheck -x` so sources resolve. Shared table/format helpers (`print_table_*`) live in `scripts/lib/common.sh`, not in individual scripts. -- **P01 Adminer** needs **both** networks: `frontend` (to publish its port) and `backend` (to reach MySQL; `backend` is `internal: true` and cannot expose ports). Its port is bound to `127.0.0.1:8080` (loopback) on purpose — it's a high-privilege DB UI. The `adminer:4.8.1` image ships no curl/wget, so its healthcheck uses bundled PHP. -- **P02 Postfix milter**: SpamAssassin `spamd` (port 783) speaks SPAMC/SPAMD, **not** the milter protocol — never point `smtpd_milters` at it (it aborts every SMTP session). SMTP-time scanning needs `spamass-milter` or a `content_filter`. Container logging needs `maillog_file = /dev/stdout` + a `postlog` service (no syslog in the image). -- **P02 php-fpm** official image forces TCP via `zz-docker.conf` (`listen = 9000`); nginx must use `fastcgi_pass 127.0.0.1:9000`, not a unix socket. -- **P02 MySQL `init.sql`**: do **not** hardcode `USE ` — the image already selects `${MYSQL_DATABASE}`. Note `mysql/*.sql` is gitignored, so `init.sql` is tracked only via the `!mysql/init.sql` exception in that project's `.gitignore`. -- **P03 target containers**: don't add a compose `command:` override — it replaces the image CMD (`sleep infinity`) and the container exits/restart-loops. Let the entrypoint's `exec "$@"` keep it alive. -- **P03 CoreDNS** is built `FROM scratch` (no shell/nc/curl) → an exec healthcheck is impossible; rely on the Corefile `health :8080` plugin + restart policy instead. - -## Conventions - -- **Bash**: `set -euo pipefail`; must pass `shellcheck -x --severity=warning` (advisory info/style notes like intentional SC2016 single-quoting are acceptable). -- **Docs**: bilingual English + Magyar; mirror both when editing prose. -- **`main` is protected**: no force-push, no deletion; required status checks **ShellCheck** + **Compose Validate** (`.github/workflows/ci.yml`). Land changes via PR. -- **Commits/PRs**: do **not** add Claude Code / AI attribution (no `Co-Authored-By` trailer, no "Generated with…" footer) — project owner's preference. +- Canonical agent rules live in **AGENTS.md** (imported above). This file adds only + Claude Code runtime notes — it must not restate policy. +- Local-only, gitignored assistant tooling (restore from git history if missing): + - `.claude/` — Claude Code settings, custom slash commands, agents, templates. + - `.agents/skills/` — portable skills (`maintaining-agent-docs`, `plan-execute-review`). +- `main` is protected: land changes via PR with **ShellCheck** + **Compose Validate** + green (see AGENTS.md → "Validation Before Completion").