Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
!docker/nginx.conf.template
!docker/health.txt
!moodle/
!moodle/.docker/
!moodle/.docker/php/
!moodle/.docker/php/runtime-policy.json
!moodle/.docker/php/check-runtime.php
!moodle/.config/
!moodle/.config/php/
!moodle/.config/php/mooship.ini
7 changes: 6 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
moodle: ["4.5", "5.3"]
moodle: ["4.3", "4.5", "5.3"]
db: [postgres, mysql]
env:
SLUG: e2e
Expand All @@ -47,6 +47,11 @@ jobs:
--db-type "${{ matrix.db }}" --moodle-version "${{ matrix.moodle }}" --profile small
--smtp-host "${gateway}:1025" --smtp-secure none --noreply-email "noreply@${DOMAIN}"

- name: Verify the actual PHP security baseline
run: |
actual=$(docker exec "${SLUG}_php" php -r 'echo PHP_VERSION;')
python3 -B lib/runtime_policy.py check-runtime "${{ matrix.moodle }}" "$actual"

- name: Login page through nginxproxy
run: |
curl --fail --silent --show-error --max-time 60 "http://${DOMAIN}/login/index.php" -o login.html
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
docker build --target web --tag shellharbor/mooship:dev-moodle4.5.15-nginx .
- name: PHP syntax
run: |
for file in docker/*.php; do
for file in docker/*.php moodle/.docker/php/check-runtime.php; do
docker run --rm --read-only --network none --volume "$PWD/$file:/check.php:ro" shellharbor/mooship:dev-moodle4.5.15-php8.3 php -l /check.php
done
- name: Validate both Compose examples
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
MOOSHIP_DOCKER_TESTS: '1'
MOOSHIP_HTTP_INTEGRATION: '1'
MOOSHIP_PROXY_INTEGRATION: '1'
MOOSHIP_TEST_PHP_IMAGE: php:8.4-fpm-alpine
MOOSHIP_TEST_PHP_IMAGE: php:8.4.27-fpm-alpine
PYTHONPATH: tests
COMPONENT: ${{ matrix.component }}
steps:
Expand All @@ -38,7 +38,7 @@ jobs:
case "$COMPONENT" in
postgres) images=(postgres:17-alpine) ;;
mysql) images=(mysql:8.4) ;;
http) images=(nginx:1.29.1-alpine php:8.4-fpm-alpine python:3.12-alpine httpd:2.4-alpine alpine/openssl:latest) ;;
http) images=(nginx:1.29.1-alpine php:8.4.27-fpm-alpine python:3.12-alpine httpd:2.4-alpine alpine/openssl:latest) ;;
proxy) images=(nginx:1.29.1-alpine) ;;
*) exit 1 ;;
esac
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

After every task with file changes, briefly record what changed and why: features, fixes, internal work, dependencies, documentation, and AI-KIT. Keep new entries under `Unreleased` until a release is confirmed; use verified dates and versions. Tasks without file changes need no entry.

## [1.2.1] - 2026-10-28

- Record the non-negotiable PHP/Moodle runtime baseline in project instructions, the public skill, context, continuity profile and ADR-0007 so future changes preserve the security policy.

- Fix PHP 8.2 database-probe diagnostics when PHP reads source from stdin: write to `php://stderr` because CLI stream constants may be absent. Preserve expected failure/partial-state exit codes and cover the lowest supported patched runtime.

- Enforce per-branch PHP security floors (8.2.35, 8.3.36, 8.4.27, 8.5.12) for CVE-2026-109618/CVE-2026-109619 and Moodle 4.3+ compatibility; remove PHP 7.4/8.0/8.1 templates, pin patched bases, and guard builds/startup/activation/restore against unsafe runtimes. Require policy-aware toolkits from the Python CLI, extend boundary/lifecycle/image tests and E2E coverage, and document migration plus obsolete 1.2.0 artifacts. Release version 1.2.1 with matching image/package/toolkit metadata.

- Finalize security-release installation instructions and record successful six-case E2E, image tests and required CI before the owner-approved publication.

## Unreleased

### AI-KIT


- 2026-10-15: require image impact assessment for behavior, dependency and configuration changes, synchronized runtime/build assets, and affected image rebuilds with relevant tests. Added a verified build-input map and reporting rules to the runtime guide and synchronized public/local AI instructions so host and standalone changes are assessed together without unnecessary rebuilds or implied publication.

- 2026-10-15: synchronized the isolated host-preflight branch/PR, local verification evidence, E2E run identity and separate Dependency Graph limitation in project context, navigation and the local continuity profile without merging the earlier image work.
Expand Down Expand Up @@ -41,6 +52,8 @@ After every task with file changes, briefly record what changed and why: feature

### Project



- 2026-10-28: require LF checkout for standalone Docker assets and verify nginx's actual Docker HEALTHCHECK independently of DB availability. Windows CRLF conversion made the original locally published nginx health payload fail; canonical Git LF export is required for corrected image packaging.
- 2026-10-28: published GitHub release v1.2.0 and mooship 1.2.0 on PyPI through Trusted Publishing; verified a fresh public package install and real checksummed toolkit installation. Merged PRs #11/#6 after successful CI/E2E, corrected main's Go-specific required checks with owner approval, and synchronized verified publication/security facts in local continuity documents. Published and verified the PHP image and corrected nginx-r1 companion in Docker Hub; the original nginx tag is superseded after reproducing its Windows CRLF health defect.
- 2026-10-28: enabled repository Dependabot alerts/Dependency Graph and verified Dependency Review now passes. Added strict release tag-to-checkout verification and a Docker Hub overwrite guard; configured the user-confirmed PyPI pending publisher and GitHub environment/variable for tokenless publication.
Expand Down
12 changes: 9 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
ARG PHP_IMAGE=php:8.3-fpm-bookworm@sha256:f768bd9142444f6126a690697b028c23f30878c3b628a7c355dfce75e29cc045
ARG PHP_IMAGE=php:8.3.36-fpm-bookworm@sha256:a1a4f6f48c2e27f4974ea21459ca6c483bd3d4fbe18579ae61571a78bbe59fff
ARG NGINX_IMAGE=nginx:1.31.0-alpine@sha256:2f07d83bf561b506400dc183b1b2003803e39efbd22451f848adaba14d28c7c7
ARG MOODLE_VERSION=4.5.15
ARG MOODLE_COMMIT=0a694ba9b8b4183bf7a45094cfa7211030db3192
ARG MOODLE_SHA256=19d8ab675769cd631b733c63914d1e318acad575763c75ef7e7e238f4180ed70

FROM ${PHP_IMAGE} AS extensions
ARG MOODLE_VERSION
ARG REDIS_VERSION=6.3.0
RUN apt-get update && apt-get install -y --no-install-recommends \
COPY moodle/.docker/php/runtime-policy.json moodle/.docker/php/check-runtime.php /opt/mooship/
RUN php /opt/mooship/check-runtime.php "${MOODLE_VERSION}" \
&& apt-get update && apt-get install -y --no-install-recommends \
libicu-dev libpq-dev libzip-dev libpng-dev libjpeg62-turbo-dev \
libfreetype6-dev libonig-dev libxml2-dev libwebp-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
Expand All @@ -19,7 +22,9 @@ FROM ${PHP_IMAGE} AS source
ARG MOODLE_VERSION
ARG MOODLE_COMMIT
ARG MOODLE_SHA256
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
COPY moodle/.docker/php/runtime-policy.json moodle/.docker/php/check-runtime.php /opt/mooship/
RUN php /opt/mooship/check-runtime.php "${MOODLE_VERSION}" \
&& apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
&& mkdir -p /opt/moodle /opt/mooship \
&& test -n "${MOODLE_SHA256}" \
&& curl --fail --location --retry 3 --output /tmp/moodle.tar.gz \
Expand Down Expand Up @@ -59,6 +64,7 @@ FROM ${PHP_IMAGE} AS app
ARG MOODLE_VERSION
ARG MOOSHIP_VERSION=dev
ARG VCS_REF=unknown
COPY --from=source /opt/mooship/runtime-policy.json /opt/mooship/check-runtime.php /opt/mooship/
LABEL org.opencontainers.image.title="Mooship Moodle runtime" \
org.opencontainers.image.source="https://github.com/shellharbor/mooship" \
org.opencontainers.image.version="${MOOSHIP_VERSION}-moodle${MOODLE_VERSION}-php8.3" \
Expand Down
13 changes: 11 additions & 2 deletions PROJECT_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ Last verified: 2026-10-28. Sources: the user's instructions, local Git metadata,
## Project overview

- **Mooship** automates Moodle installation on Ubuntu with Docker Compose and a shared reverse proxy.
- Git origin is shellharbor/mooship. Local main and origin/main contain release commit `82a658205bfdeaaa68026f924db2305bd7976b29`, tagged `v1.2.0`. GitHub confirmed v1.2.0 as the latest published release on 2026-10-28; PRs #11 and #6 are merged. Earlier primary baseline: cddc89b (v1.1.2 commit subject).
- Git origin is shellharbor/mooship. At the preceding publication, main contained release commit `82a658205bfdeaaa68026f924db2305bd7976b29`, tagged `v1.2.0`. GitHub confirmed v1.2.0 as the latest published release on 2026-10-28; PRs #11 and #6 are merged. Earlier primary baseline: cddc89b (v1.1.2 commit subject).
- Workflows: creating a site, activation/deactivation, explicit removal, full backups, and restoration into an unused directory.
- Project language: English throughout documentation, code comments, CLI help, prompts, log/error messages, configuration templates, examples, and local agent instructions. Keep future additions in English, as requested by the user.
- Chat language: Russian, according to the user's current language policy; project artifacts shown in chat retain English.
- README badges below the banner show six live `main`-branch workflow statuses (CI, CodeQL, Security, Component integration, Configuration, Documentation), the latest published release, and the MIT license. E2E and coverage-service badges are not configured.
- Public AI entry point: root `SKILL.md` (`name: mooship`) covers development, review, server operations, invariants, and validation without requiring AI-KIT. It is tracked, and README links to it for GitHub readers and normal clones. The existing `.claude/skills/deploy-moodle/SKILL.md` delegates to it but remains local and ignored, as do `.agents/` and `ai-kit/`. No global installation. The local `ai-kit/SKILLS.md` registers the public entry point.
- Published documentation includes the already tracked `PROJECT_CONTEXT.md`, `WIKI.md`, and `BOOTSTRAP_PROMPT.md` despite their ignore patterns. Optional local AI-KIT resources are named as code paths rather than public Markdown links. The documentation gate validates targets against tracked and pending non-ignored files, so local-only resources cannot hide broken links in a clean checkout.

## Mandatory runtime security baseline (2026-10-28)

- Owner instruction: PHP branch floors are **8.2.35 / 8.3.36 / 8.4.27 / 8.5.12**; prohibit PHP 8.1 and older and Moodle below **4.3**, including 4.3 itself in allowed versions. Official PHP changelog and AWS bulletin 2026-143 confirm CVE-2026-109618/CVE-2026-109619 patch levels. Earlier stack entries below are historical and do not permit obsolete runtimes.
- Canonical policy: `moodle/.docker/php/runtime-policy.json`; host selection and actual-version checks: `lib/runtime_policy.py`; image build/startup guard: `moodle/.docker/php/check-runtime.php`. Moodle 4.3 allows PHP 8.2; 4.4/4.5 allow 8.2/8.3; 5.0/5.1 allow 8.2/8.3/8.4; 5.2/5.3 allow 8.3/8.4. PHP 8.5 has no verified Moodle pair; unknown branches and prereleases fail closed.
- Host PHP 8.2/8.3/8.4 templates now use digest-pinned patched Bookworm bases. PHP 7.4/8.0/8.1 Dockerfiles and INI files are removed. Root app/web builds use patched PHP 8.3.36 for runtime/source. Activation and restore validate saved versions against actual source `version.php`, rebuild, then probe actual PHP before site startup or DB import; diagnosis/backup/removal remain available for migration. Host cron uses a Compose command to preserve the guarded image entrypoint. The Python adapter rejects pre-policy toolkits for deploy/activate/restore.
- Owner approved merging PR #14 with a one-time administrative review bypass and publishing security version **1.2.1** on 2026-10-28. Release/package references: [GitHub](https://github.com/shellharbor/mooship/releases/tag/v1.2.1), [PyPI](https://pypi.org/project/mooship/1.2.1/); image tags: `1.2.1-moodle4.5.15-php8.3` and `1.2.1-moodle4.5.15-nginx`. Registry digests and publication verification belong in the release record; VERSION alone does not establish publication. Existing Docker/PyPI/native 1.2.0 artifacts predate the policy and must not be used for new deployments. Existing installations/data are not automatically modified. Read `docs/RUNTIME_POLICY.md` and ADR-0007 for migration and maintenance.
- This task affects host PHP/cron templates and both standalone targets. Local app/web rebuilds complete on PHP 8.3.36; actual PHP 8.2.35 host-startup tests accept initial Moodle 4.3/4.3.0 and reject older/incompatible source. A second real PHP 8.2 stdin regression verifies the database probe's fixed stderr path and expected error exit. Independent security review findings (cron bypass, stale source metadata, inclusive 4.3 normalization) are resolved. Candidate validation and publication boundaries are tracked in [PR #14](https://github.com/shellharbor/mooship/pull/14).
- E2E now includes Moodle 4.3/4.5/5.3 x PostgreSQL/MySQL (six cases). Initial run 40711524928 passed all four 4.5/5.3 cases and installed both 4.3 sites, but the PHP 8.2 native-state fixture exposed missing CLI stream constants when PHP reads from stdin. `lib/check-database.php` now uses `php://stderr`; corrected run [40714114550](https://github.com/shellharbor/mooship/actions/runs/40714114550) passed all six cases at security source 531c341. All 26 PR checks and seven local real image tests passed (494.881 seconds); 144 Linux regressions passed with 19 opt-in skips. Helper coverage is 94% (runtime_policy.py 100%). Subsequent publication preparation changes documentation only; require exact-release-commit CI through the release gate.

## Verified stack

- Bash and Python 3.8+ (standard library); Ubuntu 20.04+, root, Docker Engine, and the Compose plugin.
Expand All @@ -28,7 +37,7 @@ Last verified: 2026-10-28. Sources: the user's instructions, local Git metadata,

## Standalone Moodle runtime (2026-10-15)

- Published Docker Hub pair (2026-10-28), Linux amd64: `1.2.0-moodle4.5.15-php8.3` and corrected `1.2.0-moodle4.5.15-nginx-r1` under shellharbor/mooship. App digest: `shellharbor/mooship@sha256:70387b904527873f32f948eae36e23c4b0f2d48864a4a831b4e457e82810fe77`; web digest: `shellharbor/mooship@sha256:4a37ae5202bea0569cc5eae4adf5e43f6cf21eaaaa1b08d23cf7125737da63f8`. Both use the unchanged v1.2.0 source at 82a6582. Original nginx tag is superseded: Windows CRLF conversion broke its exact Docker health probe. Corrected nginx-r1 is exported from Git with core.autocrlf=false. CLI login as shellharbor resolved the earlier insufficient_scope failures.
- Historical 1.2.0 Docker Hub pair (2026-10-28), Linux amd64: `1.2.0-moodle4.5.15-php8.3` and corrected `1.2.0-moodle4.5.15-nginx-r1` under shellharbor/mooship. App digest: `shellharbor/mooship@sha256:70387b904527873f32f948eae36e23c4b0f2d48864a4a831b4e457e82810fe77`; web digest: `shellharbor/mooship@sha256:4a37ae5202bea0569cc5eae4adf5e43f6cf21eaaaa1b08d23cf7125737da63f8`. Both use the unchanged v1.2.0 source at 82a6582. Original nginx tag is superseded: Windows CRLF conversion broke its exact Docker health probe. Corrected nginx-r1 is exported from Git with core.autocrlf=false. CLI login as shellharbor resolved the earlier insufficient_scope failures.
- Moodle 4.5.15 is pinned to commit `0a694ba9b8b4183bf7a45094cfa7211030db3192` and archive SHA-256; PHP 8.3 Bookworm and nginx 1.31.0 Alpine bases are digest-pinned. Both images contain the same immutable source. Moodle and dependency licenses remain bundled; wrapper code remains MIT.
- UID/GID 1000; writable `/var/moodledata` and `/tmp`, read-only roots, no capabilities/Docker socket/source mount required. Credentials use environment or absolute `_FILE` secrets; only the install workload receives the admin secret. Commands: `fpm`, explicit locked `install`, `cron`, `cron-once`, `check`, and passthrough `php ...`. Normal startup does not migrate schemas and refuses an absent/mismatching installed version.
- `docker/compose.yml` plus the chosen `compose.postgres.yml` or `compose.mysql.yml` overlay supplies PostgreSQL 17/MySQL 8.4 and a separate scheduler. Only nginx publishes HTTP, on loopback 8080 by default. Runtime settings/secrets, backup/upgrade differences from the host toolkit and later publication commands are in `docs/DOCKER.md` (ADR-0004).
Expand Down
Loading
Loading